Compare commits

...

359 Commits

Author SHA1 Message Date
Kimdiego2098
7cbf289b50 更新版本号 2023-11-19 22:32:53 +08:00
Kimdiego2098
4097da79a5 modbus server 添加是否立即写入内存的选项 2023-11-19 22:32:13 +08:00
Kimdiego2098
91b7ae554f 默认开启多标签 2023-11-19 22:31:47 +08:00
Kimdiego2098
3121aa2542 添加历史报警插件 2023-11-19 22:19:05 +08:00
Kimdiego2098
4bf895e6e1 修复4.0代码 s7协议未设置适配器导致初始化/读写失败的问题 2023-11-19 21:49:32 +08:00
Kimdiego2098
0c5489e920 增加上传插件 缓存基类 2023-11-19 15:20:01 +08:00
Kimdiego2098
d63c3aaa80 获取插件继承属性时,去除不重写特性的属性 2023-11-19 14:15:58 +08:00
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
2289 changed files with 166307 additions and 75874 deletions

9
.gitignore vendored
View File

@@ -362,10 +362,7 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
/src/Plugins/Other
/src/ThingsGateway.Web.Server/*.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 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.

147
README.md
View File

@@ -1,141 +1,38 @@

<div align='center'>
<img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/gitLogo.png" height=100 />
<div align="center">
# ThingsGateway
[![NuGet(ThingsGateway)](https://img.shields.io/nuget/v/ThingsGateway.Foundation.svg?label=ThingsGateway.Foundation)](https://www.nuget.org/packages/kimdiego/)
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[![star](https://gitee.com/diego2098/ThingsGateway/badge/star.svg)](https://gitee.com/diego2098/ThingsGateway/stargazers)
[![fork](https://gitee.com/diego2098/ThingsGateway/badge/fork.svg)](https://gitee.com/diego2098/ThingsGateway/members)
![qq](https://img.shields.io/badge/QQ群-605534569-blue)
## 介绍
</div>
</div>
**NetCore** 跨平台边缘采集网关(工业设备采集)
#### 介绍
**ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
基于Net6/7+Blazor Server的跨平台边缘采集网关支持南北端插件式开发
并拥有较完善的北端Rpc权限管理。
[Github地址](https://github.com/kimdiego2098/ThingsGateway)
<div >
如果对您有帮助请点击右上角⭐Star关注感谢支持开源
</div>
#### 开源说明
Apache 2.0+[附加协议](https://diego2098.gitee.io/thingsgateway-docs/docs/)
Apache 2.0 开源协议的核心内容是以保护和尊重原作者的著作权为主要目的。对使用复制修改商用不做过多限制但必须包含原著的License信息。
#### 功能亮点
- Blazor Server架构开发部署更简单
- 采集/上传配置完全支持Excel导入导出
- 插件式驱动,方便驱动二次开发
- 支持采集通道冗余,上传离线缓存
- 时序数据库存储
- 实时/历史报警(Sql转储),支持布尔/高低限值
#### 演示
http://120.24.62.140:5000/
默认账户密码superAdmin 111111
**ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
#### 社区版采集插件
> 支持分包解析/订阅
- Modbus(Rtu/Tcp/Udp)
- OPCDAClient支持导入节点
- OPCUAClient支持导入节点动态类型
- 西门子S7协议
## 文档
#### 社区版上传插件
> 支持Rpc写入
- Modbus Server
- OPCUA Server (支持历史查询)
- Mqtt Server (支持自定义json)
- Mqtt Client (支持自定义json)
- IotSharp Client (IotSharp网关插件Rpc待测试)
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
> 不支持Rpc
- RabbitMQ (支持自定义json)
- Kafka
## 协议
#### nuget
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
- Modbus库支持ModbusTcp、ModbusRtu、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
```
## 演示
- S7库
``` powershell
dotnet add package ThingsGateway.Foundation.Adapter.Siemens
```
[ThingsGateway演示地址](http://120.24.62.140:5000/)
#### 效果图
<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>
账户 : **superAdmin**
密码 : **111111**
## 赞助
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
## 社区
QQ群605534569
#### 文档
使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway-docs/)
#### 特别鸣谢
- Furion[https://dotnetchina.gitee.io/furion](https://dotnetchina.gitee.io/furion)
- SqlSugar[https://gitee.com/dotnetchina/SqlSugar](https://gitee.com/dotnetchina/SqlSugar)
- Simple.Admin[https://gitee.com/zxzyjs/SimpleAdmin](https://gitee.com/zxzyjs/SimpleAdmin)
- Masa.Blazor[https://www.masastack.com/blazor](https://www.masastack.com/blazor)
- MiniExcel[https://gitee.com/dotnetchina/MiniExcel](https://gitee.com/dotnetchina/MiniExcel)
- TouchSocket[https://gitee.com/rrqm_home/touchsocket](https://gitee.com/rrqm_home/touchsocket)
- IdGenerator[https://github.com/yitter/idgenerator](https://github.com/yitter/idgenerator)
- CodingSeb.ExpressionEvaluator[https://github.com/codingseb/ExpressionEvaluator](https://github.com/codingseb/ExpressionEvaluator)
- Hardware.Info[https://github.com/Jinjinov/Hardware.Info](https://github.com/Jinjinov/Hardware.Info)
- UAParser[https://github.com/ua-parser/uap-csharp](https://github.com/ua-parser/uap-csharp)
- OPCUAWebPlatformUniCT[https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT](https://github.com/OPCUAUniCT/OPCUAWebPlatformUniCT)
#### 补充说明
* 使用OPC相关插件时请遵循OPC基金会的授权规则
* 使用OPCDA插件时需安装OPC核心库[文件地址](https://gitee.com/diego2098/ThingsGateway/attach_files)
#### 支持作者
如果对您有帮助请点击右上角⭐Star关注感谢支持开源
若希望捐赠项目请查看以下捐赠码或使用Gitee捐赠功能
<img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/pay.png" height=180 />
#### 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

287
framework/ThingsGateway.sln Normal file
View File

@@ -0,0 +1,287 @@

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
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLHisAlarm", "plugin\ThingsGateway.Plugin.SQLHisAlarm\ThingsGateway.Plugin.SQLHisAlarm.csproj", "{0947E6C0-6371-4483-B0ED-191FB5073151}"
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
{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0947E6C0-6371-4483-B0ED-191FB5073151}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0947E6C0-6371-4483-B0ED-191FB5073151}.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}
{0947E6C0-6371-4483-B0ED-191FB5073151} = {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.1</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

@@ -10,16 +10,15 @@
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Core
namespace ThingsGateway.Admin.Core;
/// <summary>
/// 资源表常量
/// </summary>
public class ResourceConst
{
/// <summary>
/// 资源表常量
/// 系统内置编码
/// </summary>
public class ResourceConst
{
/// <summary>
/// 系统内置单页面编码
/// </summary>
public const string System = "system";
}
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

@@ -12,8 +12,9 @@
using Furion.Schedule;
namespace ThingsGateway.Web.Core;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IJobPersistence"/>
public class JobPersistence : IJobPersistence
@@ -21,30 +22,9 @@ public class JobPersistence : IJobPersistence
private readonly IServiceScope _serviceScope;
/// <inheritdoc/>
public JobPersistence(IServiceProvider serviceProvider)
public JobPersistence(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceProvider.CreateScope();
}
/// <summary>
/// 作业调度服务启动时
/// </summary>
/// <returns></returns>
public IEnumerable<SchedulerBuilder> Preload()
{
// 获取所有定义的作业
var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
return allJobs;
}
/// <summary>
/// 作业计划初始化通知
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public SchedulerBuilder OnLoading(SchedulerBuilder builder)
{
return builder;
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
@@ -59,9 +39,30 @@ public class JobPersistence : IJobPersistence
}
/// <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 = "true",
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

@@ -10,9 +10,11 @@
//------------------------------------------------------------------------------
#endregion
using Furion.DependencyInjection;
using Microsoft.AspNetCore.Components.Forms;
namespace ThingsGateway.Application;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文件管理服务

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
}

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