Compare commits

...

766 Commits

Author SHA1 Message Date
Kimdiego2098
c6aec3a1af 更新版本号 2023-11-22 14:06:52 +08:00
Kimdiego2098
22e30f7a62 代码文件编码更改 2023-11-22 14:02:41 +08:00
Kimdiego2098
57711b8ab5 修复变量上传属性在编辑页面不显示的问题 2023-11-22 13:35:44 +08:00
Kimdiego2098
90ff1259ea 修复变量上传属性在编辑页面不显示的问题 2023-11-22 13:30:42 +08:00
Kimdiego2098
d88fc5ccd7 去除不存在的菜单 2023-11-21 18:19:14 +08:00
Kimdiego2098
5aaca2aa9c 调整循环延时时间限值 2023-11-21 16:27:11 +08:00
Kimdiego2098
8b9ca56e17 调试mqtt依赖 2023-11-21 15:07:07 +08:00
Kimdiego2098
e4f3772e6d 去除上传间隔1s限制 2023-11-21 11:46:33 +08:00
Kimdiego2098
d58ec81d20 整理代码 2023-11-21 11:41:44 +08:00
Kimdiego2098
415aae44b6 更新依赖 2023-11-21 08:44:47 +08:00
Kimdiego2098
a533286658 更新版本号 2023-11-20 23:05:12 +08:00
Kimdiego2098
e59f91cd82 去除treeview 2023-11-20 23:04:54 +08:00
Kimdiego2098
5f8b85d8a4 去除treeview显示 2023-11-20 22:53:34 +08:00
Kimdiego2098
47c7b88436 更新版本号 2023-11-20 22:47:18 +08:00
Kimdiego2098
90006782f2 mqttserver/client插件支持websocket通道,直接对接前端 2023-11-20 22:42:08 +08:00
Kimdiego2098
c3d49cbe70 mqttserver/client插件支持websocket通道,直接对接前端 2023-11-20 22:33:26 +08:00
Kimdiego2098
112323a360 modbusTcpServer最大连接数设为3w 2023-11-20 19:46:07 +08:00
Kimdiego2098
9d08c90fda 更新依赖 2023-11-20 17:35:42 +08:00
Kimdiego2098
602d24deec 添加admin-解决方案 2023-11-20 15:38:33 +08:00
Kimdiego2098
a2b9f66785 修复设备curd服务aop失效的问题 2023-11-19 23:30:11 +08:00
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
Kimdiego2098
9d570f5b45 更新icon包版本 2023-07-23 00:03:46 +08:00
Kimdiego2098
af7fafd34f 更新icon包版本 2023-07-22 23:59:03 +08:00
Kimdiego2098
d43130f4fc 修改插件日志为自定义日志 2023-07-22 23:34:09 +08:00
Kimdiego2098
7500194620 更新文档 2023-07-22 20:48:29 +08:00
Kimdiego2098
eb27c29144 发布1.7.4 2023-07-22 20:47:26 +08:00
Kimdiego2098
43260b3e24 修正错误名称 2023-07-22 20:32:34 +08:00
Kimdiego2098
f80713f0aa 添加上传插件最后错误原因 2023-07-22 20:10:45 +08:00
Kimdiego2098
0c4bdc7ad1 修复上传设备暂停按钮失效 2023-07-22 19:56:56 +08:00
Kimdiego2098
811cff7bd0 OPCUAServer 数组维度不再写any;rpc写入添加延时 2023-07-22 17:02:44 +08:00
Kimdiego2098
30269aa75c 更改假死检测间隔5分钟 2023-07-22 11:05:56 +08:00
Kimdiego2098
e345ef7083 添加OPCUAServer线程间隔配置项,优化最后一次错误提示 2023-07-22 11:03:56 +08:00
Kimdiego2098
f559c9b8f7 支持OPCUAServer数组类型,添加缓存文件存在判断 2023-07-21 21:28:39 +08:00
Kimdiego2098
f4af0916b2 去除OPCUA写入数组维度校验 2023-07-20 17:37:49 +08:00
Kimdiego2098
f15f14f28d 更新文档 2023-07-20 12:48:13 +08:00
Kimdiego2098
834f44f58d kafka尝试自动加载c库 2023-07-20 12:40:59 +08:00
Kimdiego2098
b36f45dcf4 修复中间变量Rpc写入 2023-07-20 10:55:37 +08:00
Kimdiego2098
11ba21c9a8 ModbusServer添加自定义循环间隔,修复中间变量写入 2023-07-20 10:55:17 +08:00
Kimdiego2098
b045557ce1 更新文档 2023-07-19 18:02:31 +08:00
Kimdiego2098
0dd251a3f6 更新文档 2023-07-19 15:43:28 +08:00
Kimdiego2098
793acb1725 更改左侧菜单为手风琴效果 2023-07-19 15:00:40 +08:00
Kimdiego2098
921243e8bd 更新文档 2023-07-19 14:34:27 +08:00
Kimdiego2098
bd9d7a90d9 修改OPCUAClient心跳频率默认3s 2023-07-18 14:49:27 +08:00
Kimdiego2098
cc444a4cea 更改OPCUAClient心跳频率为30000 2023-07-18 14:15:02 +08:00
Kimdiego2098
38ca1fa168 更新1.7.3版本 2023-07-18 12:16:33 +08:00
Kimdiego2098
7a552b87ec 更新masa 稳定版以及其他包 2023-07-18 12:11:37 +08:00
Kimdiego2098
36923d3190 修复添加订阅时,值死区过滤逻辑 2023-07-18 11:13:21 +08:00
Kimdiego2098
a9d3017123 修复异步锁上下文切换导致OPCUA 心跳事件出错 2023-07-18 09:58:01 +08:00
Kimdiego2098
313acd4976 修复OPCUAClient调试界面重复输出订阅值 2023-07-18 08:51:27 +08:00
Kimdiego2098
a4c91bb268 修改OPCUAClient的心跳频率配置项 2023-07-18 08:48:18 +08:00
Kimdiego2098
f9b566984b 添加重启锁 2023-07-17 21:40:47 +08:00
2248356998 qq.com
8dd261854d Merge branch 'master' of https://gitee.com/diego2098/thingsgateway-docs 2023-07-17 20:59:53 +08:00
2248356998 qq.com
7351e62d87 更新opcua心跳状态日志 2023-07-17 20:59:32 +08:00
Diego2098
0593ae720b 更新文档 2023-07-16 20:46:16 +08:00
Diego2098
a0a7b08e08 更新授权名单 2023-07-16 20:31:56 +08:00
2248356998 qq.com
9a3bc6b8b3 更新文档 2023-07-16 18:27:22 +08:00
2248356998 qq.com
5acae17f71 更新文档 2023-07-16 18:12:55 +08:00
2248356998 qq.com
f1e5b76ef2 更新文档 2023-07-16 18:07:31 +08:00
2248356998 qq.com
53c628fde9 更新文档 2023-07-16 17:49:33 +08:00
2248356998 qq.com
baca0a70c0 更新文档 2023-07-16 17:48:22 +08:00
2248356998 qq.com
3e8d0af404 更新文档 2023-07-16 17:41:52 +08:00
2248356998 qq.com
cf9a91d9d5 更新文档 2023-07-16 17:35:04 +08:00
2248356998 qq.com
02b9e282c6 更新文档地址 2023-07-16 17:32:49 +08:00
2248356998 qq.com
9ce87f235f 迁移文档 2023-07-16 17:28:26 +08:00
2248356998 qq.com
e329bea1b2 冗余设备删除后会导致后台出错 2023-07-16 11:36:45 +08:00
2248356998 qq.com
8086e7b54d 更新readme 2023-07-16 10:28:43 +08:00
2248356998 qq.com
f7a875606e 删除无用属性 2023-07-16 09:40:00 +08:00
2248356998 qq.com
196eaf85f4 修复1.7.0版本修改导致的mqttrpc映射错误 2023-07-15 22:56:28 +08:00
2248356998 qq.com
876a55668e 增加 上传插件的列表分割大小,因为某些情况下传输字节太大会导致失败 2023-07-15 22:42:34 +08:00
2248356998 qq.com
05bd21bdd5 导入提示的当前行显示未初始 2023-07-15 22:35:12 +08:00
2248356998 qq.com
fb51a08cc6 格式化整理 2023-07-15 22:32:54 +08:00
2248356998 qq.com
dd83d7f4d3 插件报文截取前200字符,防止页面渲染过多 2023-07-15 20:04:14 +08:00
2248356998 qq.com
842a56f7ce kafka插件增加超时选项 2023-07-15 17:27:35 +08:00
2248356998 qq.com
9246a6e797 历史服务修复变量在线状态显示错误 2023-07-15 15:46:39 +08:00
2248356998 qq.com
8ad693f717 发布1.7.2版本 2023-07-15 11:10:29 +08:00
2248356998 qq.com
f4c2ee7cc4 优化OPCUA错误提示 2023-07-14 17:33:11 +08:00
2248356998 qq.com
6043441faa OPCUA在取消订阅时应该走读取方法 2023-07-14 17:22:19 +08:00
2248356998 qq.com
4a065c3710 优化导入excel提示 2023-07-14 16:23:42 +08:00
2248356998 qq.com
0ef800bdd7 优化导入excel提示 2023-07-14 16:23:35 +08:00
2248356998 qq.com
56eaa1910d kafka 插件释放时取消事件注册 2023-07-14 14:35:40 +08:00
2248356998 qq.com
201788e286 更改属性说明 2023-07-14 12:23:46 +08:00
2248356998 qq.com
506e0f144f 添加kafka插件null传播 2023-07-14 10:41:04 +08:00
2248356998 qq.com
72f68bfdd9 opcda JValue转object 2023-07-14 10:14:05 +08:00
2248356998 qq.com
2f9869b11d opcua读取值JValue转为object 2023-07-14 10:04:05 +08:00
2248356998 qq.com
8ffcf6498c RabbitMQ,IotSharp添加离线缓存 2023-07-13 19:54:12 +08:00
2248356998 qq.com
d224ae1923 Variable Value数据类型改为object 2023-07-13 18:18:53 +08:00
2248356998 qq.com
fed2063a19 中间变量页种子ID重复 2023-07-13 17:47:22 +08:00
2248356998 qq.com
db2810cdd7 复制设备没有及时刷新缓存 2023-07-13 17:38:45 +08:00
2248356998 qq.com
4f1a6781ef 发布1.7.1 2023-07-13 11:19:17 +08:00
2248356998 qq.com
beffa5d5a4 代码格式化 2023-07-13 11:18:36 +08:00
2248356998 qq.com
7a20f1de07 OPCDA增加数组支持,增加写入动态类型支持 2023-07-13 11:16:31 +08:00
2248356998 qq.com
cd25cf726b 优化OPCUA数组类型转换 2023-07-13 09:02:36 +08:00
2248356998 qq.com
d6b1bc3842 修复blazor界面null错误 2023-07-12 22:12:11 +08:00
2248356998 qq.com
a4385fb9bb 更新ReadMe 2023-07-12 21:33:05 +08:00
2248356998 qq.com
7045f2b8ea 删除重复文件 2023-07-12 21:30:41 +08:00
2248356998 qq.com
07ca1a4de8 更新nuget包 2023-07-12 21:21:50 +08:00
2248356998 qq.com
24f289e692 V1.7.0发布
1、增加采集通道冗余
2、优化多个界面
3、导出导入功能优化
4、增加中间变量
5、OPCUAClient支持动态类型
6、离线缓存多处覆盖,包含上传插件/历史报警/时序库
7、增加通用调试界面,增加s7调试
8、其他优化
2023-07-12 21:16:38 +08:00
2248356998 qq.com
01bcdaae2d 修复AppDataTable清空MForm模型导致筛选列清空的问题 2023-07-10 13:18:14 +08:00
2248356998 qq.com
55890008d1 修复写入s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 17:01:44 +08:00
2248356998 qq.com
5ab9b01879 修复读取s7协议 bit值 偏移错误(以byte数据块为准) 2023-07-08 11:44:57 +08:00
2248356998 qq.com
e4abb333b3 默认添加kafka插件 种子数据 2023-07-07 17:13:00 +08:00
2248356998 qq.com
09f476c745 修复布尔量解析时反转字节导致结果值不符的错误 2023-07-06 14:05:09 +08:00
2248356998 qq.com
8806e68dce null传播 2023-07-03 14:13:00 +08:00
2248356998 qq.com
2ef1e25cd8 添加x86架构 2023-07-03 10:48:52 +08:00
2248356998 qq.com
10e7f202aa 补充插件实例内容 2023-07-02 19:50:09 +08:00
2248356998 qq.com
ccd7000c09 blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:42:19 +08:00
2248356998 qq.com
8ee7b798cf blazor组件初始化并行执行,导致sugar单例DB出现线程偶发错误,暂增加copyNew()解决 2023-06-29 16:36:03 +08:00
2248356998 qq.com
7733cf5bf0 sqlsugar偶发线程故障,添加copyNew方法 2023-06-28 18:54:45 +08:00
2248356998 qq.com
a05ce86dd7 登录跳转uri优化 2023-06-28 15:47:35 +08:00
2248356998 qq.com
91f51c32e8 更新开源说明 2023-06-28 10:48:33 +08:00
2248356998 qq.com
f910202bba update openapiUser datatableUI 2023-06-26 19:51:02 +08:00
2248356998 qq.com
6d77194a8f css脚本结果不采用 System.Text.Json.JsonSerializer 2023-06-26 19:44:17 +08:00
2248356998 qq.com
9deb89c15f OPCUAClient修复当变量为string类型时的数值过滤失败导致添加订阅失效 2023-06-26 19:43:00 +08:00
2248356998 qq.com
4b62a092b4 停用采集设备时,变量获取运行态出错 2023-06-26 16:43:48 +08:00
2248356998 qq.com
81c8f626f9 登录后跳转原url 2023-06-26 14:46:24 +08:00
2248356998 qq.com
3e846c42fb cookie授权验证失败时返回登录界面 2023-06-26 14:36:41 +08:00
Diego2098
63ad7fd766 更新nuget类库 2023-06-25 18:59:09 +08:00
2248356998 qq.com
9ff1e9aa34 添加报警事件None枚举 2023-06-20 13:10:58 +08:00
2248356998 qq.com
8d162b6f3d 修复cpu核心等于1时报错,修正登录错误提示 2023-06-20 09:09:08 +08:00
Diego2098
9844d10bef 修复modbusServer 初始化错误 2023-06-17 18:14:13 +08:00
2248356998 qq.com
b908fa8489 sugar添加取消令箭 2023-06-15 17:38:36 +08:00
2248356998 qq.com
15a10643a7 增加变量运行态 CollectVariableRuntime 所在设备属性 2023-06-15 16:50:08 +08:00
2248356998 qq.com
299617aca1 parallel.foreach无序体验不好,退回为foreach 2023-06-14 11:04:09 +08:00
2248356998 qq.com
45647d697a update 1.6.1 version 2023-06-13 22:39:47 +08:00
2248356998 qq.com
48f5105d38 update nuget package 2023-06-13 22:39:25 +08:00
2248356998 qq.com
fe1c741d68 update driver messagesui 2023-06-13 22:26:19 +08:00
2248356998 qq.com
fa42cc1f00 并行关闭线程 2023-06-12 14:41:08 +08:00
2248356998 qq.com
42cf5e7a81 添加mqtt/kafka上传内容显示 2023-06-12 14:40:53 +08:00
2248356998 qq.com
47905e1aa1 优化大量变量excel上传的验证过程 2023-06-12 11:35:27 +08:00
2248356998 qq.com
9a8e907df3 更新文档站点地址 2023-06-11 18:37:50 +08:00
2248356998 qq.com
106fe85582 删除docs 2023-06-11 18:22:15 +08:00
2248356998 qq.com
4b3571bd57 更新1.6.0版本 2023-06-11 17:58:57 +08:00
2248356998 qq.com
96b537401a 缓存最大默认2000 2023-06-11 17:56:34 +08:00
2248356998 qq.com
721c9eb057 添加离线缓存大小限制配置 2023-06-11 17:55:03 +08:00
2248356998 qq.com
51701bf6d6 上传插件线程等待时间改为10ms 2023-06-11 17:47:16 +08:00
2248356998 qq.com
dbde68bd56 导入变量优化 2023-06-11 17:46:23 +08:00
2248356998 qq.com
ad2c9f585a 添加mqttClient离线缓存 2023-06-11 17:45:46 +08:00
2248356998 qq.com
562093c468 添加kafka离线缓存 2023-06-11 17:45:27 +08:00
Diego2098
b0295584a3 !6 部分配置会导致SqlServer自动建库失败
Merge pull request !6 from samisgod/master
2023-06-10 06:55:34 +00:00
samisgod
208c54de98 fix db init for SqlServer 2023-06-10 14:51:29 +08:00
2248356998 qq.com
63e2d941a1 增加pwa 2023-06-09 17:53:41 +08:00
2248356998 qq.com
3956838e9c 修复停用验证码时登录失败显示null报错 2023-06-09 17:09:17 +08:00
2248356998 qq.com
abeee58bb0 统一文件编码 2023-06-09 15:04:54 +08:00
2248356998 qq.com
d5b1b49722 update solution folder 2023-06-09 14:30:53 +08:00
2248356998 qq.com
564ed03ff8 upload deviceStatusPage 2023-06-09 10:32:11 +08:00
2248356998 qq.com
70db4c76b4 update deviceStatusPage 2023-06-09 09:49:03 +08:00
2248356998 qq.com
d059f7975b remove dotNET China declaration 2023-06-09 09:10:30 +08:00
2248356998 qq.com
4e74e6dc2d add dotNET China declaration 2023-06-09 09:02:17 +08:00
Diego2098
b6deb96658 重启线程时增加运行状态界面空传播防止报错 2023-06-08 21:35:07 +08:00
2248356998 qq.com
3839e966be add upload plugin code description 2023-06-08 17:47:53 +08:00
2248356998 qq.com
3dd035849c 迁移导入变量功能到驱动调试内 2023-06-08 15:11:58 +08:00
2248356998 qq.com
3d6532b5d6 plugin unload test,but failed 2023-06-08 10:52:39 +08:00
2248356998 qq.com
bf7c175ee7 更改默认api文档为Knife4j 2023-06-07 20:34:31 +08:00
2248356998 qq.com
f84af35ed6 缓存键增加Type-TypeHandle句柄 2023-06-07 18:42:58 +08:00
2248356998 qq.com
99063b3eb1 修改OPCUA证书路径,增加默认接收不收信任证书与其选项;
OPCDA整理;
2023-06-07 18:03:57 +08:00
2248356998 qq.com
3bec18f28d 重启线程WebApi修改 2023-06-07 11:32:18 +08:00
2248356998 qq.com
15de7a7894 添加注释提示 2023-06-07 11:28:11 +08:00
2248356998 qq.com
e20e04e677 GC策略更改,大量变量实例手动清空以快速内存释放 2023-06-07 11:19:24 +08:00
2248356998 qq.com
5fc6ae2835 上传1.5.1 2023-06-06 19:37:04 +08:00
2248356998 qq.com
7d281b8c96 Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-06-05 08:33:50 +08:00
Diego2098
4880b801a7 S7-TCP连接修复死锁 2023-06-02 22:20:21 +08:00
2248356998 qq.com
74e354456a 增加KINGVIEW 配置 2023-05-29 01:32:37 +08:00
2248356998 qq.com
af2e03aa36 超管用户名称不可更改 2023-05-26 00:14:05 +08:00
2248356998 qq.com
d8fa660ab6 初步添加OPCUAClient调试界面;更新依赖 2023-05-25 23:41:11 +08:00
2248356998 qq.com
1a62d48297 OPCUA安全策略添加全部选项 2023-05-25 00:59:21 +08:00
2248356998 qq.com
7ba01be13d OPCUA安全策略添加全部选项 2023-05-25 00:58:47 +08:00
2248356998 qq.com
1a83d64db7 添加OPCDAClient调试页面 2023-05-25 00:11:00 +08:00
2248356998 qq.com
5b53014c40 修改程序根目录为文件所在目录 2023-05-24 14:37:45 +08:00
2248356998 qq.com
83685340af 默认添加服务守护支持 2023-05-24 14:32:21 +08:00
2248356998 qq.com
31e0cc4dec ModbusServer添加自定义数据类型;修复发布时静态文件路径错误 2023-05-24 12:31:20 +08:00
2248356998 qq.com
56b87fc1f5 Add copyright notices 2023-05-23 23:54:28 +08:00
2248356998 qq.com
6b956a2dd7 update TGTcpClient 2023-05-23 22:42:48 +08:00
2248356998 qq.com
1937623d7d 添加Modbus系列插件调试页面;添加Modbus组包解析缓存超时时间; 2023-05-23 20:50:44 +08:00
2248356998 qq.com
3b60b10945 update HardwareInfoService 2023-05-23 12:04:04 +08:00
2248356998 qq.com
7173acd350 nuget更新 2023-05-23 11:59:28 +08:00
2248356998 qq.com
6310d87338 硬件界面添加限值防报错 2023-05-23 11:46:19 +08:00
2248356998 qq.com
49a1ed7c18 初步添加插件驱动调试页面 2023-05-22 18:50:30 +08:00
2248356998 qq.com
d426e280d9 初步添加插件驱动调试页面 2023-05-22 18:41:09 +08:00
2248356998 qq.com
6154fb29f1 Razor文件格式化清理 2023-05-22 18:40:50 +08:00
2248356998 qq.com
97d48ef9d6 删除调试代码... 2023-05-22 17:09:21 +08:00
2248356998 qq.com
88992625c4 更改变量读取间隔限制最低为10ms 2023-05-22 15:17:22 +08:00
2248356998 qq.com
bc6eb44218 明确System.Management版本 2023-05-22 14:51:02 +08:00
2248356998 qq.com
cf9ccd799d 硬件信息获取添加异常拦截 2023-05-22 14:33:30 +08:00
2248356998 qq.com
ffa0e4e771 update adapter 2023-05-22 12:42:31 +08:00
2248356998 qq.com
60fa9c196c 代码清理 2023-05-21 22:39:33 +08:00
2248356998 qq.com
df860d22fb 优化opcua质量戳提示;添加数据转换基础方法;TCP等待返回时默认断开连接立即返回 2023-05-21 10:51:56 +08:00
2248356998 qq.com
cb46ff326c modbus 组包优化 2023-05-20 21:41:16 +08:00
2248356998 qq.com
f277a853ef 类型更换 2023-05-20 17:13:14 +08:00
2248356998 qq.com
9ae34f67c3 可指定OPCUA节点数据类型 2023-05-20 15:40:17 +08:00
2248356998 qq.com
c9223218cc 更新文档 2023-05-20 13:52:23 +08:00
2248356998 qq.com
c0dd645aba 添加自定义OPCUAServer数据类型 2023-05-20 12:34:06 +08:00
2248356998 qq.com
2e948eb5b6 导入规则优化,主动抛出名称重复错误 2023-05-19 22:55:53 +08:00
2248356998 qq.com
c3276889cf 更改插件时开启刷新设备属性 2023-05-19 22:26:37 +08:00
2248356998 qq.com
a76ca8282d 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:27:20 +08:00
2248356998 qq.com
8ce6b8362f 判断OPCUAServer状态时增加tryCatch 2023-05-19 21:24:56 +08:00
2248356998 qq.com
842fb12f05 优化tcp拆包组包 2023-05-19 20:28:51 +08:00
2248356998 qq.com
d63e1511af 快捷方式null错误 2023-05-19 16:33:57 +08:00
2248356998 qq.com
278783b8e0 更新依赖包 2023-05-19 16:23:21 +08:00
2248356998 qq.com
d24e3c922d UDP通讯优化 2023-05-19 16:21:42 +08:00
2248356998 qq.com
1d02cd2283 添加链路锁 2023-05-19 14:27:47 +08:00
2248356998 qq.com
8edeb82a87 分包数量显示错误 2023-05-19 10:13:48 +08:00
2248356998 qq.com
146e9279de Modbus读写锁更新 2023-05-19 10:05:54 +08:00
2248356998 qq.com
47105f50a9 Modbus读写锁更新 2023-05-19 09:53:56 +08:00
2248356998 qq.com
16c9c80f37 删除sqlsugar旧版本代码,会导致sqlite不兼容 2023-05-19 09:15:32 +08:00
2248356998 qq.com
8e7e4bc95a 适配mysql;修复写入表达式转换;优化web页面写入体验 2023-05-18 23:38:40 +08:00
2248356998 qq.com
0aa3d2f930 数据库初始化修复 2023-05-18 22:24:27 +08:00
2248356998 qq.com
ce77755a1e 数据库连接自动释放 2023-05-18 21:06:06 +08:00
2248356998 qq.com
0f31f20c87 编辑页面添加 变量 允许远程写入选项 2023-05-18 21:03:44 +08:00
2248356998 qq.com
ee6da2aaa5 修复枚举类型在mysql中类型出错的问题 2023-05-18 20:58:14 +08:00
Diego2098
a35f087cd9 修改文件大小限制 2023-05-17 22:29:59 +08:00
Diego2098
6e029b44dd 更新设备读写锁 2023-05-17 22:05:31 +08:00
2248356998 qq.com
973c0cff34 touchSocket修复UDP在windows下重置连接的问题;更新nuget 2023-05-17 16:54:33 +08:00
2248356998 qq.com
2027eea6ac 代码格式清理 2023-05-17 16:49:25 +08:00
2248356998 qq.com
2f43692f33 修复CancellationTokenSource未释放导致Linux下偶发内存问题 2023-05-17 16:44:52 +08:00
2248356998 qq.com
6d24992f88 OPCUAServer节点数据类型增加在读取表达式转换后的判断 2023-05-16 09:05:58 +08:00
Diego2098
b4388a58d6 ModbusClient添加帧前时间 2023-05-15 22:45:55 +08:00
2248356998 qq.com
158aa05fac 修复using作用域导致获取的服务可能被释放的问题 2023-05-15 19:21:35 +08:00
2248356998 qq.com
f2731bf55e nuget更新 2023-05-15 18:55:27 +08:00
2248356998 qq.com
7304e99fce mqttClient同步间隔上传的实体类 2023-05-12 18:22:06 +08:00
2248356998 qq.com
02700b83eb update mqttClient 2023-05-12 16:40:55 +08:00
2248356998 qq.com
676b25acf9 MqttClient增加间隔上传选项;更改线程循环间隔说明定义 2023-05-12 16:31:57 +08:00
Diego2098
556359ea2d update readme 2023-05-10 21:18:43 +08:00
Diego2098
b72923e0f5 增加api控制采集启停等方法 2023-05-10 21:17:34 +08:00
Diego2098
115ac9f75e 提交kafka插件 2023-05-10 21:17:09 +08:00
2248356998 qq.com
32e36f6708 update readme 2023-05-10 17:49:52 +08:00
2248356998 qq.com
d949b7a4f9 共享链路写入变量修复;Nuget更新 2023-05-10 16:33:59 +08:00
2248356998 qq.com
eae1171ff5 更新文档 2023-05-09 17:51:55 +08:00
2248356998 qq.com
76a1b75a51 更新文档 2023-05-09 17:47:22 +08:00
2248356998 qq.com
8882c0daea OPCUAClient/Server 修改证书有效期为100年 2023-05-09 14:37:18 +08:00
2248356998 qq.com
07ebc16d59 添加控制台logo 2023-05-08 18:10:07 +08:00
2248356998 qq.com
0ceb109964 Code Cleanup 2023-05-08 18:03:32 +08:00
2248356998 qq.com
118b0d0038 add GetSciptValue 2023-05-08 17:45:13 +08:00
2248356998 qq.com
5e87067792 modbus rtu 粘包优化 2023-05-08 15:59:35 +08:00
2248356998 qq.com
c946a252e8 modbus rtu报文粘包优化;共享链路切换延时;上传插件添加报文界面 2023-05-08 15:57:33 +08:00
2248356998 qq.com
f9ad2ba1dd plc read with CancellationToken 2023-05-08 13:33:01 +08:00
2248356998 qq.com
0d0ecd33bd 粘包优化 2023-05-08 10:55:16 +08:00
2248356998 qq.com
e4b98fd05b Rpc条件bug修复 2023-05-07 18:06:27 +08:00
2248356998 qq.com
95a5933303 OPCUAServer修复匿名登录 2023-05-07 18:06:09 +08:00
2248356998 qq.com
da3b55fa64 更新文档 2023-05-07 16:56:41 +08:00
2248356998 qq.com
fbbabfb90e 属性顺序调整 2023-05-05 09:46:11 +08:00
2248356998 qq.com
f13da6830d 更新文档 2023-05-04 22:58:36 +08:00
2248356998 qq.com
f560a8e2f8 update CollectDeviceThread 2023-05-04 10:58:45 +08:00
2248356998 qq.com
56f1139c2f 更新文档 2023-05-02 22:11:55 +08:00
2248356998 qq.com
773bdfc1e2 更新1.5.0 2023-05-02 22:06:09 +08:00
2248356998 qq.com
f449666628 1、共享链路支持;
2、设备报文查看;
3、采集线程重构;
2023-05-02 21:58:11 +08:00
2248356998 qq.com
3f282de0ab mqtt重连锁优化 2023-04-27 11:19:32 +08:00
2248356998 qq.com
440dd8d22f 添加常用转换 2023-04-24 17:54:00 +08:00
2248356998 qq.com
dcff9de2f7 masa更新 2023-04-24 11:16:28 +08:00
2248356998 qq.com
a192866543 更新包 2023-04-24 09:26:47 +08:00
Diego2098
10081416de 报警后台服务去除多余接口
Signed-off-by: Diego2098 <2248356998@qq.com>
2023-04-19 00:48:13 +00:00
2248356998 qq.com
e2bed618f9 添加docker文件 2023-04-17 17:39:12 +08:00
2248356998 qq.com
03ab1f3823 单文件发布 2023-04-17 15:13:40 +08:00
2248356998 qq.com
ac8aeb63d9 update SqlSugarConfig 2023-04-17 11:58:41 +08:00
2248356998 qq.com
2e16d822fa 删除其他信息 2023-04-17 11:05:38 +08:00
2248356998 qq.com
e407d873fa 验证码更新修复 2023-04-17 09:05:44 +08:00
2248356998 qq.com
fd712a1dbe 1、字段null约束修改
2、ModbusServer绑定端口优化
2023-04-16 15:02:09 +08:00
2248356998 qq.com
e9028b40ce 更新readme 2023-04-16 13:34:29 +08:00
2248356998 qq.com
c9da3dee7c 更新1.4.0
注意Excel导入已不适用以前版本
1、去除动态更新插件
2、改用MiniExcel,支持动态导入,excel配置更简单
3、优化多处界面与后台逻辑,部分方法规范更名
4、修复外网地址获取错误等
2023-04-15 20:48:56 +08:00
2248356998 qq.com
c8c224e202 修复报警文本逻辑 2023-04-11 13:40:13 +08:00
2248356998 qq.com
f34559daaf 1,修复控制台报错(echarts.js问题,已删除)2,多处Dispose修正 2023-04-07 08:48:34 +08:00
2248356998 qq.com
9fefbf4c27 过滤 2023-04-06 14:46:24 +08:00
2248356998 qq.com
1af9fd73ea 历史数据库选择为sqlite时查询转换日期错误 2023-04-05 18:04:33 +08:00
2248356998 qq.com
75ef394eff 启用开发环境web详细日志,调整App.Razor位置,添加网页ico 2023-04-05 17:14:26 +08:00
2248356998 qq.com
ec6cc2c63e update console/file datetime format 2023-04-05 15:49:00 +08:00
2248356998 qq.com
06bc2e192b 更新readme 2023-04-04 18:41:56 +08:00
2248356998 qq.com
78701ec7c1 😀版本1.3.1 2023-04-04 17:55:52 +08:00
2248356998 qq.com
c925fab7e4 网关软件时间统一UTC 2023-04-04 17:55:15 +08:00
2248356998 qq.com
42fd72c164 update iotSharpClient 2023-04-04 11:07:05 +08:00
2248356998 qq.com
7fd160e1a2 😀 更新1.3.0 2023-04-04 10:15:34 +08:00
2248356998 qq.com
97a0d940eb update iotSharpClient 2023-04-04 09:25:16 +08:00
2248356998 qq.com
efaa099d81 update iotSharpClient 2023-04-04 09:23:22 +08:00
2248356998 qq.com
47864a804b IosSharpClient Rpc优化 2023-04-04 09:22:22 +08:00
2248356998 qq.com
91136c0e43 IotSharp Rpc方法完善 2023-04-04 09:19:48 +08:00
2248356998 qq.com
28c3b1bd61 添加写入多个变量的api方法 2023-04-04 09:18:58 +08:00
2248356998 qq.com
551352bc40 update IotSharpClient 2023-04-03 20:16:38 +08:00
2248356998 qq.com
e73c24c925 更新种子 2023-04-03 19:34:12 +08:00
2248356998 qq.com
7ec4c286cc 添加IotSharp插件 2023-04-03 19:30:52 +08:00
2248356998 qq.com
6705e2ec4b 分页显示令牌 2023-04-03 15:29:35 +08:00
2248356998 qq.com
6f0373063b 后台启动时Furion RootServices NULL值 2023-04-03 15:19:59 +08:00
2248356998 qq.com
f64eef60b5 修复不存在采集设备时,初始化报警/历史服务bug 2023-04-03 14:12:56 +08:00
2248356998 qq.com
89546bf86b 弹窗消息在SignalR订阅方法中需InvokeAsync 2023-04-03 13:35:14 +08:00
2248356998 qq.com
793678feca 修复规范化结果包装2次导致登录返回结果不正确的问题 2023-04-03 12:45:19 +08:00
2248356998 qq.com
923cc3019a 更新演示地址 2023-04-03 10:47:49 +08:00
2248356998 qq.com
10eb98a5f6 readme 2023-04-02 18:12:18 +08:00
2248356998 qq.com
bd9e89d8dd readme 2023-04-02 18:08:56 +08:00
2248356998 qq.com
1926b4ce73 更新readme 2023-04-02 18:05:54 +08:00
2248356998 qq.com
4ef3062d74 更新readme 2023-04-02 18:05:28 +08:00
2248356998 qq.com
abb6e0f60f 更新包 2023-04-02 17:10:32 +08:00
2248356998 qq.com
f204d8d84e 添加注释 2023-04-02 16:59:46 +08:00
2248356998 qq.com
fa301656f1 调整依赖,添加关系图 2023-04-01 17:28:35 +08:00
2248356998 qq.com
7e1221028f 调整依赖,更新版本1.2.1 2023-04-01 15:45:02 +08:00
2248356998 qq.com
41308cb2dd 整理 2023-04-01 13:57:57 +08:00
2248356998 qq.com
130600521c 😀 OPCUAServer支持历史查询数据 2023-03-31 18:32:55 +08:00
2248356998 qq.com
cd57548a48 添加ThingsGateway.Foundation注释 2023-03-31 16:25:33 +08:00
2248356998 qq.com
efacc99f76 硬件信息获取添加延时 2023-03-30 20:51:08 +08:00
2248356998 qq.com
f0d236e172 脚本显示优化 2023-03-30 19:51:38 +08:00
2248356998 qq.com
a8118bd8c6 更新文档 2023-03-30 19:39:35 +08:00
2248356998 qq.com
0e58f2ef53 mqtt/mq上传 添加上传实体自定义脚本 2023-03-30 19:07:18 +08:00
2248356998 qq.com
f4b22b3a0c 表达式整理 2023-03-30 16:23:03 +08:00
2248356998 qq.com
df5bd281c7 更新版本 2023-03-30 14:12:39 +08:00
2248356998 qq.com
a3f23837ce 更新文档 2023-03-30 14:11:16 +08:00
2248356998 qq.com
612d989b97 readme 2023-03-30 14:06:03 +08:00
2248356998 qq.com
42c01ee9a2 更新readme,nuget 2023-03-30 14:05:00 +08:00
2248356998 qq.com
14074db591 删除多余代码 2023-03-30 13:53:57 +08:00
2248356998 qq.com
43dfdd7942 opcua 写入添加用户名日志 2023-03-30 13:32:32 +08:00
2248356998 qq.com
f397b97ccf 去除ua数据类型验证 2023-03-30 13:20:26 +08:00
2248356998 qq.com
95f8716144 更新readme 2023-03-30 13:13:44 +08:00
2248356998 qq.com
17ba472b2e 类命名错误更改 2023-03-30 13:11:46 +08:00
2248356998 qq.com
42d82571ab 发布文件添加 2023-03-30 13:11:43 +08:00
2248356998 qq.com
9119a28141 添加OPCUAServer插件,修复个别bug 2023-03-30 13:10:31 +08:00
Diego2098
a32263d838 1、OPCUAClient修复客户端证书未自动生成
2、修复异步方法错误使用
2023-03-30 00:15:13 +08:00
2248356998 qq.com
208ae2bb88 添加部分代码注释 2023-03-29 16:22:01 +08:00
2248356998 qq.com
4d85462a85 驱动支持主机名称 2023-03-29 11:07:21 +08:00
2248356998 qq.com
f601aa9ca0 修复初始化失败导致的一系列问题 2023-03-29 10:53:42 +08:00
2248356998 qq.com
8aee3ad455 上传插件间隔时间修正 2023-03-28 16:40:00 +08:00
2248356998 qq.com
6a2a1e9561 历史变量enable失效 2023-03-28 16:38:29 +08:00
2248356998 qq.com
5f8786c9dc 历史报表时间格式改为yyyy-MM-dd HH:mm:ss ffffff 2023-03-28 16:31:53 +08:00
2248356998 qq.com
73f1d3eead 采集设备状态判断错误 2023-03-28 16:21:30 +08:00
2248356998 qq.com
2bf21bb3c3 采集设备状态判断错误 2023-03-28 15:55:01 +08:00
2248356998 qq.com
f80f0dbb11 修复历史保存值 频繁时出现值相同的问题 2023-03-28 15:14:30 +08:00
2248356998 qq.com
37518c70c4 opc 活动时间修正 2023-03-28 14:15:14 +08:00
2248356998 qq.com
e5951b5bef 添加写入采集时间的接口,而不是固定DateTime.Now 2023-03-28 14:03:11 +08:00
2248356998 qq.com
ab320bd90b 更新tcpclient 2023-03-28 13:52:33 +08:00
2248356998 qq.com
7bd36b5371 修复linux 分隔符错误问题 2023-03-28 11:48:16 +08:00
2248356998 qq.com
b882b0f2bc 更改插件文件不存在时的日志信息 2023-03-28 10:21:39 +08:00
2248356998 qq.com
38d7ae73cc 更改项目引用结构 2023-03-28 10:21:23 +08:00
2248356998 qq.com
4527c6ee5d swagger文档 2023-03-27 19:01:04 +08:00
Iot边缘设备
85829e70c1 !5 添加部署文档
* 添加部署文档
2023-03-27 10:22:30 +00:00
2248356998 qq.com
256c08d82a 属性/字段缓存获取只包含public 2023-03-27 14:49:09 +08:00
2248356998 qq.com
c2ce03c047 更新masa-1.0.0-preview.10 2023-03-27 14:27:15 +08:00
2248356998 qq.com
f2af19e198 优化变量上传属性页 2023-03-27 12:00:10 +08:00
2248356998 qq.com
930b7c092d 采集设备活动时间应正常刷新 2023-03-27 10:51:54 +08:00
2248356998 qq.com
00757c69c6 添加设备复制功能 2023-03-27 10:42:02 +08:00
2248356998 qq.com
55f267d0fc update code collation 2023-03-27 08:49:10 +08:00
2248356998 qq.com
6b96aff6e8 update code collation 2023-03-26 19:48:08 +08:00
2248356998 qq.com
32b773a8fa 更新nuget包 2023-03-26 18:52:16 +08:00
2248356998 qq.com
03089adad6 发行1.10版本 2023-03-26 18:10:12 +08:00
2248356998 qq.com
4a1fe746ab 实时数据界面优化 2023-03-26 17:39:05 +08:00
2248356998 qq.com
aa52c05d2c 1、采用异步AutoResetEvent,修复多个设备下采集太慢的问题
2、弃用IntelligentConcurrentQueue(touchsocket集成类)
2023-03-26 17:32:29 +08:00
2248356998 qq.com
26407a43e7 1、优化设备状态页,减少signalr刷新所需包大小
2、日志前置等级添加
2023-03-24 18:32:10 +08:00
2248356998 qq.com
a02934bf19 修复导出表格时,null值报错 2023-03-23 22:09:53 +08:00
2248356998 qq.com
09c65fba09 导入错误时,忽略大于10行的错误信息显示 2023-03-23 20:01:19 +08:00
2248356998 qq.com
4305c727d0 更新readme 2023-03-22 15:29:35 +08:00
2248356998 qq.com
188339897f 优化log等级、json配置注解 2023-03-22 15:26:42 +08:00
2248356998 qq.com
4ecff9a707 OPCUA写入测试 2023-03-22 14:06:32 +08:00
2248356998 qq.com
355aed49c6 设备运行状态页面改为扩展 2023-03-22 13:46:23 +08:00
2248356998 qq.com
4717b6b0f0 更新文档 2023-03-22 10:44:44 +08:00
Diego2098
45ebe9048d rbmq插件优化 2023-03-21 23:38:00 +08:00
Diego2098
b2170c49a3 分包失败提示 2023-03-21 23:28:30 +08:00
Diego2098
dc2f4d6115 设备组列宽调整,补充上传设备组 2023-03-21 23:10:59 +08:00
Diego2098
1eb132440f 1、更新DataTypeEnum
2、OPCUA写入时需实际数据类型
2023-03-21 22:47:10 +08:00
Diego2098
a464bbc37a 更新Tests 2023-03-21 22:46:12 +08:00
2248356998 qq.com
ed995697c2 表格无数据时,修改默认宽度 2023-03-21 18:17:18 +08:00
2248356998 qq.com
163cd84c7b S7nuget版本 2023-03-21 15:44:50 +08:00
2248356998 qq.com
293d7cc292 masa nuget版本更改 2023-03-21 14:31:36 +08:00
2248356998 qq.com
5de1b4e74c GC策略修改 2023-03-21 14:07:17 +08:00
2248356998 qq.com
7b474975da 更新文档 2023-03-21 14:03:49 +08:00
2248356998 qq.com
beab51516b 更新文档 2023-03-21 13:56:14 +08:00
2248356998 qq.com
fe8685a50c 修改s7属性注释 2023-03-21 13:51:05 +08:00
2248356998 qq.com
f9af5d0885 更新readme 2023-03-21 13:49:10 +08:00
2248356998 qq.com
e8136a9720 更新S7协议插件 2023-03-21 13:45:58 +08:00
2248356998 qq.com
531e5d4556 修改S7特殊方法 2023-03-21 13:41:32 +08:00
2248356998 qq.com
e66255963a 修改s7 2023-03-21 13:38:43 +08:00
2248356998 qq.com
246aac8ee4 添加西门子S7协议插件 2023-03-21 13:37:17 +08:00
2248356998 qq.com
23cfeff685 版本恢复 2023-03-21 11:45:18 +08:00
2248356998 qq.com
a5e7e0d126 更新nuget,暂缓链路复用功能 2023-03-20 13:34:59 +08:00
2248356998 qq.com
5bebc30ba0 默认不开启转储 2023-03-20 09:22:11 +08:00
2248356998 qq.com
0e7057f5b9 更新opcda文档 2023-03-19 23:18:24 +08:00
2248356998 qq.com
7c6c365ba4 格式清理 2023-03-19 23:12:58 +08:00
2248356998 qq.com
424c9bb0c5 修复null值报错 2023-03-19 23:02:08 +08:00
2248356998 qq.com
9d0f26594c 添加设备组,变量按设备组别搜索 2023-03-19 22:56:02 +08:00
2248356998 qq.com
99c17de079 更新readme,opcda核心库提示 2023-03-19 20:40:54 +08:00
2248356998 qq.com
b1e3dd0af6 尝试修复中文在mac上乱码 2023-03-19 17:29:01 +08:00
2248356998 qq.com
261cb89530 修正中文在mac上乱码 2023-03-19 16:45:14 +08:00
2248356998 qq.com
ff6773ba37 删除未知文件 2023-03-19 16:35:24 +08:00
2248356998 qq.com
bdfbbfcbbd Merge branch 'master' of https://gitee.com/diego2098/ThingsGateway 2023-03-19 16:34:26 +08:00
2248356998 qq.com
0c4cd56758 添加设备组功能 2023-03-19 16:34:15 +08:00
士心
4a36658321 !4 修复MacOS环境下,数据库报错的问题
* 修复macos环境下,数据库报错
2023-03-19 08:28:54 +00:00
2248356998 qq.com
7aae938685 准备更新设备组/变量组功能 2023-03-19 11:49:06 +08:00
2248356998 qq.com
3723401e7a 更新ReadMe 2023-03-18 17:56:18 +08:00
2248356998 qq.com
70631366a9 1、最后校验失败时应该提示日志
2、暂停时设备状态修正
2023-03-18 17:49:08 +08:00
2248356998 qq.com
0e40bbda3e 更新文档 2023-03-18 16:47:29 +08:00
2248356998 qq.com
e9aa475398 ByteBlock实际长度 2023-03-18 16:45:39 +08:00
2248356998 qq.com
8d2a811184 修正串口描述类ToString 2023-03-18 16:32:15 +08:00
2248356998 qq.com
dd7f5b6700 更新文档 2023-03-18 16:23:23 +08:00
2248356998 qq.com
a4f6277737 更新nuget 2023-03-18 16:16:27 +08:00
2248356998 qq.com
c2bfaacbb7 更新readme 2023-03-18 16:15:14 +08:00
2248356998 qq.com
a17cbfa2d4 添加ModbusRtu种子数据 2023-03-18 16:14:37 +08:00
2248356998 qq.com
fb9a101555 添加ModbusRtu插件 2023-03-18 16:14:25 +08:00
2248356998 qq.com
e319cf0200 添加串口基础类 2023-03-18 16:14:08 +08:00
2248356998 qq.com
0a8395ef6a 更新文档 2023-03-17 18:19:14 +08:00
2248356998 qq.com
38df5e01be 更新文档 2023-03-17 17:52:04 +08:00
2248356998 qq.com
ebd891a868 种子数据更改 2023-03-17 17:51:58 +08:00
2248356998 qq.com
4ab2395cbe 采集检测间隔修改 2023-03-17 17:40:34 +08:00
2248356998 qq.com
5f1f989fc9 上传插件添加循环间隔属性 2023-03-17 17:30:06 +08:00
2248356998 qq.com
44b709eee3 添加rbmq插件种子数据 2023-03-17 16:30:02 +08:00
2248356998 qq.com
d0d7726597 rbmq插件文件夹更名 2023-03-17 16:16:23 +08:00
2248356998 qq.com
054c342aeb 优化内存queue,修复mqtt插件可能超出字节限制的情况 2023-03-17 16:04:24 +08:00
2248356998 qq.com
c79c33baf7 添加rbmq插件 2023-03-17 16:03:44 +08:00
2248356998 qq.com
23b00e35b2 设备禁用时,变量绑定的设备对应选项会显示禁用(灰色) 2023-03-17 11:29:14 +08:00
2248356998 qq.com
fe51079266 更新readme 2023-03-16 17:33:25 +08:00
2248356998 qq.com
0791b0bbee 选择插件时自动更新属性 2023-03-16 17:33:17 +08:00
2248356998 qq.com
dbf04c8eeb 更新赞助名单 2023-03-16 11:35:58 +08:00
2248356998 qq.com
6204256df8 限流服务默认不开启 2023-03-16 09:47:20 +08:00
2248356998 qq.com
93cc8c2327 增加简易定时看板 2023-03-15 16:01:54 +08:00
2248356998 qq.com
68a2e5bbbc 更新历史配置说明 2023-03-15 09:11:33 +08:00
Diego2098
72792153f2 !3 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Merge pull request !3 from zhubanghao/N/A
2023-03-14 13:39:16 +00:00
zhubanghao
88b6ef1897 update handbook/docs/05、网关配置/5.5、其他配置.mdx.
Signed-off-by: zhubanghao <58813184@qq.com>
2023-03-14 13:05:33 +00:00
2339 changed files with 187084 additions and 92250 deletions

9
.gitignore vendored
View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,3 +1,4 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -186,7 +187,7 @@
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.

View File

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

View File

@@ -0,0 +1,101 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.6.33927.249
MinimumVisualStudioVersion = 10.0.40219.1
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("{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}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}"
ProjectSection(SolutionItems) = preProject
..\.gitignore = ..\.gitignore
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{6961511A-8787-42AF-827D-B630B2AF4791}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{268A1A81-2685-47E1-9986-5934A58A31A4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{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
{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
{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{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}
{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
{6961511A-8787-42AF-827D-B630B2AF4791} = {268A1A81-2685-47E1-9986-5934A58A31A4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
EndGlobalSection
EndGlobal

288
framework/ThingsGateway.sln Normal file
View File

@@ -0,0 +1,288 @@

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
..\README.md = ..\README.md
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.4</Version>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>Diego</Authors>
<Product>ThingsGateway</Product>
<Copyright>© 2023-present Diego</Copyright>
<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
<SignAssembly>True</SignAssembly>
<DelaySign>False</DelaySign>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,62 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// 后台登录控制器
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("auth/b")]
[LoggingMonitor]
public class AuthController : IDynamicApiController
{
private readonly IAuthService _authService;
/// <summary>
/// <inheritdoc cref="AuthController"/>
/// </summary>
/// <param name="authService"></param>
public AuthController(IAuthService authService)
{
_authService = authService;
}
/// <summary>
/// 后台登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("login")]
[Description(EventSubscriberConst.Login)]
public async Task<LoginOutput> LoginAsync(LoginInput input)
{
return await _authService.LoginAsync(input);
}
/// <summary>
/// 后台登出
/// </summary>
/// <returns></returns>
[HttpPost("logout")]
[Description(EventSubscriberConst.Logout)]
[Authorize]
public async Task LogoutAsync()
{
await _authService.LogoutAsync();
}
}

View File

@@ -0,0 +1,67 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// 文件下载
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
[Route("file")]
[LoggingMonitor]
public class FileController : IDynamicApiController
{
private readonly IOperateLogService _operateLogService;
private readonly IVisitLogService _visitLogService;
/// <summary>
/// <inheritdoc cref="FileController"/>
/// </summary>
public FileController(
IOperateLogService operateLogService,
IVisitLogService visitLogService
)
{
_operateLogService = operateLogService;
_visitLogService = visitLogService;
}
/// <summary>
/// 下载操作日志
/// </summary>
/// <returns></returns>
[HttpGet("operateLog")]
public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input)
{
var memoryStream = await _operateLogService.ExportFileAsync(input);
var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
};
return data;
}
/// <summary>
/// 下载访问日志
/// </summary>
/// <returns></returns>
[HttpGet("visitLog")]
public async Task<IActionResult> DownloadVisitLogAsync([FromQuery] VisitLogInput input)
{
var memoryStream = await _visitLogService.ExportFileAsync(input);
var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
};
return data;
}
}

View File

@@ -0,0 +1,63 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace ThingsGateway.Admin.ApiController;
/// <summary>
/// OpenApi登录控制器
/// </summary>
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
[Route("auth/openapi")]
[LoggingMonitor]
[Description("OpenApi登录")]
[Authorize(AuthenticationSchemes = "Bearer")]
public class OpenApiAuthController : IDynamicApiController
{
private readonly IOpenApiAuthService _authService;
/// <summary>
/// <inheritdoc cref="OpenApiAuthController"/>
/// </summary>
/// <param name="authService"></param>
public OpenApiAuthController(IOpenApiAuthService authService)
{
_authService = authService;
}
/// <summary>
/// OpenApi登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpPost("login")]
[Description(EventSubscriberConst.LoginOpenApi)]
public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
{
return await _authService.LoginOpenApiAsync(input);
}
/// <summary>
/// 登出
/// </summary>
/// <returns></returns>
[HttpPost("logout")]
[Description(EventSubscriberConst.LogoutOpenApi)]
public async Task LogoutAsync()
{
await _authService.LogoutAsync();
}
}

View File

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

View File

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

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
</ItemGroup>
</Project>

View File

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

View File

@@ -0,0 +1,43 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 操作事件说明特性
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class OperDescAttribute : Attribute
{
/// <summary>
/// 操作记录标识
/// </summary>
/// <param name="description"></param>
/// <param name="catcategory"></param>
public OperDescAttribute(string description, string catcategory = LogConst.LOG_OPERATE)
{
Description = description;
Catcategory = catcategory;
}
/// <summary>
/// 分类
/// </summary>
public string Catcategory { get; }
/// <summary>
/// 说明
/// </summary>
public string Description { get; }
/// <summary>
/// 记录参数默认true
/// </summary>
public bool IsRecordPar { get; set; } = true;
}

View File

@@ -0,0 +1,224 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.Reflection;
using Furion.Reflection.Extensions;
using System.Reflection;
using System.Text;
using ThingsGateway.Foundation.Extension.String;
using UAParser;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// AOP处理操作日志
/// </summary>
public class OperDispatchProxy : AspectDispatchProxy, IDispatchProxy
{
/// <summary>
/// 服务提供器可以用来解析服务Services.GetService()
/// </summary>
public IServiceProvider Services { get; set; }
/// <summary>
/// 当前服务实例
/// </summary>
public object Target { get; set; }
/// <summary>
/// 方法
/// </summary>
/// <param name="method"></param>
/// <param name="args"></param>
/// <returns></returns>
public override object Invoke(MethodInfo method, object[] args)
{
var desc = Target.GetCustomAttribute<OperDescAttribute>(method.ToString(), true);
if (desc == null)
{
return Invoke(method, args);
}
else
{
Exception exception = default;
object result = default;
try
{
result = Invoke(method, args);
}
catch (Exception ex)
{
exception = ex;
}
WriteOperLog(method, args, desc, result, exception);
if (exception != null)
{
throw exception;
}
return result;//返回结果
}
object Invoke(MethodInfo method, object[] args)
{
//如果不带返回值
if (method.ReturnType == typeof(void))
{
return method.Invoke(Target, args);//直接返回
}
else
{
var result = method.Invoke(Target, args);
return result;//返回结果
}
}
}
/// <summary>
/// 异步无返回值
/// </summary>
/// <param name="method"></param>
/// <param name="args"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public override async Task InvokeAsync(MethodInfo method, object[] args)
{
var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target);
if (desc == null)
{
var task = method.Invoke(Target, args) as Task;
await task;
}
else
{
Exception exception = default;
try
{
var task = method.Invoke(Target, args) as Task;
await task;
}
catch (Exception ex)
{
exception = ex;
}
WriteOperLog(method, args, desc, null, exception);
if (exception != null)
{
throw exception;
}
}
}
/// <summary>
/// 异步带返回值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="method"></param>
/// <param name="args"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public override async Task<T> InvokeAsyncT<T>(MethodInfo method, object[] args)
{
var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target);
if (desc == null)
{
var taskT = method.Invoke(Target, args) as Task<T>;
var result = await taskT;
return result;//返回结果
}
else
{
T result = default;
//写入操作日志
Exception exception = null;
try
{
var taskT = method.Invoke(Target, args) as Task<T>;
result = await taskT;
}
catch (Exception ex)
{
exception = ex;
}
WriteOperLog(method, args, desc, result, exception);
if (exception != null)
{
throw exception;
}
else
{
return result;//返回结果
}
}
}
private void WriteOperLog(MethodInfo method, object[] args, OperDescAttribute desc, object result, Exception exception)
{
//写入操作日志
var str = App.HttpContext?.Request?.Headers?.UserAgent;
ClientInfo clientInfo = null;
if (str.HasValue)
{
var uaParser = Parser.GetDefault();
clientInfo = uaParser.Parse(str);
}
StringBuilder stringBuilder = new();
if (desc.IsRecordPar)
{
var parameters = method.GetParameters();
var jsonParameters = parameters.Select((p, i) => $"\"{p.Name}\": {args[i].ToJsonString()}");
stringBuilder.Append('{');
stringBuilder.Append(string.Join(", ", jsonParameters));
stringBuilder.Append('}');
}
var paramJson = stringBuilder.ToString();
var resultJson = desc.IsRecordPar ? result?.ToJsonString() : null;
//操作日志表实体
var log = new SysOperateLog
{
Name = desc.Description,
Category = desc.Catcategory,
ExeStatus = LogConst.LOG_SUCCESS,
OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString(),
OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
OpTime = DateTimeExtensions.CurrentDateTime,
OpAccount = UserManager.UserAccount,
ReqUrl = "",
ReqMethod = LogConst.LOG_REQMETHOD,
ResultJson = resultJson,
ClassName = method.ReflectedType.Name,
MethodName = method.Name,
ParamJson = paramJson,
VerificatId = UserManager.VerificatId.ToLong(),
};
//如果异常不为空
if (exception != null)
{
log.ExeStatus = LogConst.LOG_FAIL;//操作状态为失败
log.ExeMessage = exception.Source + ":" + exception.Message + Environment.NewLine + exception.StackTrace;
}
DbContext.Db.CopyNew().InsertableWithAttr(log).ExecuteCommand();//入库
}
}

View File

@@ -0,0 +1,38 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 权限操作常量
/// </summary>
public class AdminConst
{
#region
/// <summary>
/// 禁用操作
/// </summary>
public const string Disable = "禁用";
/// <summary>
/// 启用操作
/// </summary>
public const string Enable = "启用";
/// <summary>
/// 用户授权操作
/// </summary>
public const string GrantRole = "授权";
#endregion
}

View File

@@ -0,0 +1,83 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// Cache常量
/// </summary>
public class CacheConst
{
/// <summary>
/// 登录验证码缓存Key
/// </summary>
public const string LOGIN_CAPTCHA = "LOGIN_CAPTCHA";
/// <summary>
/// 配置缓存Key
/// </summary>
public const string SYS_CONFIGCATEGORY = "SYS_CONFIGCATEGORY";
#region OpenApi
/// <summary>
/// OpenApi用户表缓存Key
/// </summary>
public const string CACHE_OPENAPIUSER = "CACHE_OPENAPIUSER";
/// <summary>
/// OpenApi关系缓存Key
/// </summary>
public const string CACHE_OPENAPIUSERACCOUNT = "CACHE_OPENAPIUSERACCOUNT";
/// <summary>
/// UserVerificat缓存Key
/// </summary>
public const string CACHE_OPENAPIUSERVERIFICAT = "CACHE_OPENAPIUSERVERIFICAT";
/// <summary>
/// UserVerificat缓存Key
/// </summary>
public const string CACHE_USERVERIFICAT = "CACHE_USERVERIFICAT";
#endregion OpenApi
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string CACHE_SYSUSER = "CACHE_SYSUSER";
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string CAHCE_SYSUSERACCOUNT = "CAHCE_SYSUSERACCOUNT";
/// <summary>
/// 关系表缓存Key
/// </summary>
public const string CACHE_SYSRELATION = "CACHE_SYSRELATION";
/// <summary>
/// 资源表缓存Key
/// </summary>
public const string CACHE_SYSRESOURCE = "CACHE_SYSRESOURCE";
/// <summary>
/// 角色表缓存Key
/// </summary>
public const string CACHE_SYSROLE = "CACHE_SYSROLE";
}

View File

@@ -0,0 +1,61 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 其他分类常量
/// </summary>
public static class CateGoryConst
{
/// <summary>
/// ThingsGateway.Admin
/// </summary>
public const string ThingsGatewayAdmin = "ThingsGateway.Admin";
/// <summary>
/// ThingsGateway.OpenApi
/// </summary>
public const string ThingsGatewayOpenApi = "ThingsGateway.OpenApi";
#region
/// <summary>
/// 用户主页
/// </summary>
public const string Relation_SYS_USER_DEFAULTRAZOR = "Relation_SYS_USER_DEFAULTRAZOR";
/// <summary>
/// 用户工作台数据
/// </summary>
public const string Relation_SYS_USER_WORKBENCH_DATA = "SYS_USER_WORKBENCH_DATA";
/// <summary>
/// 角色有哪些权限
/// </summary>
public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
/// <summary>
/// 角色有哪些资源
/// </summary>
public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
/// <summary>
/// 用户有哪些角色
/// </summary>
public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
#endregion
}

View File

@@ -0,0 +1,96 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 配置常量
/// </summary>
public static class ConfigConst
{
/// <summary>
/// 系统固定配置
/// </summary>
public const string SYS_CONFIGBASEDEFAULT = "SYS_CONFIGBASEDEFAULT";
/// <summary>
/// 其他自定义配置
/// </summary>
public const string SYS_CONFIGOTHER = "SYS_CONFIGOTHER";
#region config
/// <summary>
/// 版权标识
/// </summary>
public const string CONFIG_COPYRIGHT = "CONFIG_COPYRIGHT";
/// <summary>
/// 版权跳转url
/// </summary>
public const string CONFIG_COPYRIGHT_URL = "CONFIG_COPYRIGHT_URL";
/// <summary>
/// 是否启用PageTab
/// </summary>
public const string CONFIG_PAGETAB = "CONFIG_PAGETAB";
/// <summary>
/// 登录验证码开关
/// </summary>
public const string CONFIG_CAPTCHA_OPEN = "CONFIG_CAPTCHA_OPEN";
/// <summary>
/// 默认用户密码
/// </summary>
public const string CONFIG_PASSWORD = "CONFIG_PASSWORD";
/// <summary>
/// 登录界面的介绍文本
/// </summary>
public const string CONFIG_REMARK = "CONFIG_REMARK";
/// <summary>
/// 单用户登录开关
/// </summary>
public const string CONFIG_SINGLE_OPEN = "CONFIG_SINGLE_OPEN";
/// <summary>
/// swagger用户
/// </summary>
public const string CONFIG_SWAGGER_NAME = "CONFIG_SWAGGER_NAME";
/// <summary>
/// swagger密码
/// </summary>
public const string CONFIG_SWAGGER_PASSWORD = "CONFIG_SWAGGER_PASSWORD";
/// <summary>
/// 系统标题
/// </summary>
public const string CONFIG_TITLE = "CONFIG_TITLE";
/// <summary>
/// 系统登录过期时间
/// </summary>
public const string CONFIG_VERIFICAT_EXPIRES = "CONFIG_VERIFICAT_EXPIRES";
/// <summary>
/// Swagger是否需要登录
/// </summary>
public const string CONFIG_SWAGGERLOGIN_OPEN = "CONFIG_SWAGGERLOGIN_OPEN";
#endregion
}

View File

@@ -0,0 +1,47 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 事件总线常量
/// </summary>
public class EventSubscriberConst
{
/// <summary>
/// 清除用户缓存
/// </summary>
public const string ClearUserCache = "清除用户缓存";
/// <summary>
/// 页面登录
/// </summary>
public const string Login = "登录";
/// <summary>
/// OpenApi登录
/// </summary>
public const string LoginOpenApi = "OpenApi登录";
/// <summary>
/// 后台登出
/// </summary>
public const string Logout = "退出";
/// <summary>
/// OpenApi登出
/// </summary>
public const string LogoutOpenApi = "OpenApi退出";
}

View File

@@ -0,0 +1,24 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 通讯器常量
/// </summary>
public class HubConst
{
/// <summary>
/// 系统HubUrl
/// </summary>
public const string SysHubUrl = "/hubs/thingsgateway";
}

View File

@@ -0,0 +1,69 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 日志常量
/// </summary>
public class LogConst
{
#region
/// <summary>
/// 登录
/// </summary>
public const string LOG_LOGIN = "LOGIN";
/// <summary>
/// 登出
/// </summary>
public const string LOG_LOGOUT = "LOGOUT";
/// <summary>
/// 第三方登录
/// </summary>
public const string LOG_OPENAPILOGIN = "OPENAPILOGIN";
/// <summary>
/// 第三方登出
/// </summary>
public const string LOG_OPENAPILOGOUT = "OPENAPILOGOUT";
/// <summary>
/// 第三方操作来源
/// </summary>
public const string LOG_OPENAPIOPERATE = "OPENAPIOPERATE";
/// <summary>
/// 操作分类
/// </summary>
public const string LOG_OPERATE = "OPERATE";
/// <summary>
/// 内部操作来源
/// </summary>
public const string LOG_REQMETHOD = "BLAZORSERVER";
/// <summary>
/// 操作成功
/// </summary>
public const string LOG_SUCCESS = "SUCCESS";
/// <summary>
/// 操作失败
/// </summary>
public const string LOG_FAIL = "FAIL";
#endregion
}

View File

@@ -0,0 +1,24 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Core;
/// <summary>
/// 资源表常量
/// </summary>
public class ResourceConst
{
/// <summary>
/// 系统内置编码
/// </summary>
public const string System = "system";
}

View File

@@ -0,0 +1,44 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色常量
/// </summary>
public class RoleConst
{
/// <summary>
/// 超级管理员
/// </summary>
public const string SuperAdmin = "superAdmin";
#region
/// <summary>
/// 角色有哪些权限
/// </summary>
public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
/// <summary>
/// 角色有哪些资源
/// </summary>
public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
/// <summary>
/// 用户有哪些角色
/// </summary>
public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
#endregion
}

View File

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

View File

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

View File

@@ -0,0 +1,32 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.Schedule;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 清理日志作业任务
/// </summary>
[JobDetail("job_log", Description = "清理访问/操作日志", GroupName = "default", Concurrent = false)]
[Daily(TriggerId = "trigger_log", Description = "清理访问/操作日志", RunOnStart = true)]
public class LogJob : IJob
{
/// <inheritdoc/>
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
var db = DbContext.Db.CopyNew();
var daysAgo = App.GetConfig<int?>("Logging:LogJob:DaysAgo") ?? 30;
await db.DeleteableWithAttr<SysVisitLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除访问日志
await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除操作日志
}
}

View File

@@ -0,0 +1,49 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录事件参数
/// </summary>
public class LoginOpenApiEvent
{
/// <summary>
/// 时间
/// </summary>
public DateTime DateTime = DateTimeExtensions.CurrentDateTime;
/// <summary>
/// 登录设备
/// </summary>
public AuthDeviceTypeEnum Device { get; set; }
/// <summary>
/// 过期时间(分)
/// </summary>
public int Expire { get; set; }
/// <summary>
/// Ip地址
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 用户信息
/// </summary>
public OpenApiUser OpenApiUser { get; set; }
/// <summary>
/// 验证Id
/// </summary>
public long VerificatId { get; set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,24 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录返回参数
/// </summary>
public class LoginOpenApiOutput : BaseLoginOutput
{
/// <summary>
/// TOKEN
/// </summary>
public string Token { get; set; }
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,55 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话输出
/// </summary>
public class OpenApiSessionOutput : PrimaryKeyEntity
{
/// <summary>
/// 账号
///</summary>
[Description("账号")]
[DataTable(Order = 1, IsShow = true, Sortable = true)]
public virtual string Account { get; set; }
/// <summary>
/// 最新登录ip
///</summary>
[Description("最新登录ip")]
[DataTable(Order = 2, IsShow = true, Sortable = true)]
public string LatestLoginIp { get; set; }
/// <summary>
/// 最新登录时间
///</summary>
[Description("最新登录时间")]
[DataTable(Order = 3, IsShow = true, Sortable = true)]
public DateTime? LatestLoginTime { get; set; }
/// <summary>
/// 令牌数量
/// </summary>
[Description("令牌数量")]
[DataTable(Order = 4, IsShow = true, Sortable = false)]
public int VerificatCount { get; set; }
/// <summary>
/// 令牌信息集合
/// </summary>
[Description("令牌列表")]
public List<VerificatInfo> VerificatSignList { get; set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,148 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// Api授权资源树
/// </summary>
public class OpenApiPermissionTreeSelector
{
/// <summary>
/// 接口描述
/// </summary>
[Description("Api说明")]
public string ApiName { get; set; }
/// <summary>
/// 路由名称
/// </summary>
[Description("Api路径")]
public string ApiRoute { get; set; }
/// <summary>
/// 子节点
/// </summary>
public List<OpenApiPermissionTreeSelector> Children { get; set; } = new();
/// <summary>
/// ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 父ID
/// </summary>
public long ParentId { get; set; }
/// <summary>
/// 权限名称
/// </summary>
[Description("权限名称")]
public string PermissionName { get; set; }
/// <summary>
/// 多个树转列表
/// </summary>
public static List<OpenApiPermissionTreeSelector> TreeToList(IList<OpenApiPermissionTreeSelector> data)
{
List<OpenApiPermissionTreeSelector> list = new();
foreach (var item in data)
{
list.Add(item);
if (item.Children != null && item.Children.Count > 0)
{
list.AddRange(TreeToList(item.Children));
}
}
return list;
}
}
/// <summary>
/// 添加用户参数
/// </summary>
public class OpenApiUserAddInput : OpenApiUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// 密码
/// </summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
public override string Password { get; set; }
/// <summary>
/// <inheritdoc/>
/// </summary>
public override bool UserEnable { get; set; } = true;
}
/// <summary>
/// 编辑用户参数
/// </summary>
public class OpenApiUserEditInput : OpenApiUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
/// <summary>
/// 密码
/// </summary>
[Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
public override string Password { get; set; }
}
/// <summary>
/// 用户分页查询参数
/// </summary>
public class OpenApiUserPageInput : BasePageInput
{
/// <summary>
/// 动态查询条件
/// </summary>
public Expressionable<SysUser> Expression { get; set; }
}
/// <summary>
/// 用户授权参数
/// </summary>
public class OpenApiUserGrantPermissionInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long? Id { get; set; }
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "PermissionList不能为空")]
public List<string> PermissionList { get; set; }
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,145 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统配置种子数据
/// </summary>
public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
{
/// <inheritdoc/>
public IEnumerable<SysConfig> SeedData()
{
List<SysConfig> configList = new List<SysConfig>
{
new SysConfig
{
Id = 22222222222222,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGER_NAME,
ConfigValue = "admin",
Remark = "swagger账号",
SortCode = 1,
},
new SysConfig
{
Id = 22222222222223,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGER_PASSWORD,
ConfigValue = "123456",
Remark = "swagger密码",
SortCode = 2,
},
new SysConfig
{
Id = 22222222222224,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_SWAGGERLOGIN_OPEN,
ConfigValue = "false",
Remark = "swagger开启登录",
SortCode = 3,
},
new SysConfig
{
Id = 22222222222226,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_TITLE,
ConfigValue = "ThingsGateway",
Remark = "标题",
SortCode = 5,
},
new SysConfig
{
Id = 22222222222228,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_COPYRIGHT,
ConfigValue = "ThingsGateway ©2023 Diego",
Remark = "系统版权",
SortCode = 6,
},
new SysConfig
{
Id = 22222222222299,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_COPYRIGHT_URL,
ConfigValue = "https://gitee.com/diego2098/ThingsGateway",
Remark = "系统版权链接地址",
SortCode = 7,
},
new SysConfig
{
Id = 22222222222229,
Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
ConfigKey = ConfigConst.CONFIG_PAGETAB,
ConfigValue = "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

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

View File

@@ -0,0 +1,91 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加菜单参数
/// </summary>
public class MenuAddInput : SysResource, IValidatableObject
{
/// <summary>
/// 路径
/// </summary>
public override string Component { get; set; }
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Icon不能为空")]
public override string Icon { get; set; }
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public override long ParentId { get; set; }
/// <summary>
/// 菜单类型
/// </summary>
public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "Title不能为空")]
public override string Title { get; set; }
/// <summary>
/// 特殊验证
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
//如果菜单类型是菜单
if (TargetType == TargetTypeEnum.SELF)
{
if (string.IsNullOrEmpty(Component))
yield return new ValidationResult("路径不能为空", new[] { nameof(Component) });
}
//设置分类为菜单
Category = ResourceCategoryEnum.MENU;
}
}
/// <summary>
/// 编辑菜单输入参数
/// </summary>
public class MenuEditInput : MenuAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 菜单树查询参数
/// </summary>
public class MenuPageInput : BasePageInput
{
/// <summary>
/// 父ID
/// </summary>
[Required(ErrorMessage = "ParentId不能为空")]
public long ParentId { get; set; }
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,43 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// <inheritdoc cref="INoticeService"/>
/// </summary>
public class NoticeService : INoticeService
{
private readonly IServiceScope _serviceScope;
public NoticeService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public async Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message)
{
//客户端ID列表
var clientIds = new List<string>();
//遍历cancellationToken列表获取客户端ID列表
verificatInfos.ForEach(it =>
{
clientIds.AddRange(it.ClientIds);
});
//获取signalr实例
var signalr = _serviceScope.ServiceProvider.GetService<IHubContext<SysHub, ISysHub>>();
//发送其他客户端登录消息
await signalr.Clients.Users(clientIds).Logout(message);
}
}

View File

@@ -0,0 +1,27 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 操作日志分页输入
/// </summary>
public class OperateLogPageInput : VisitLogPageInput
{
}
/// <summary>
/// 操作日志分页输入
/// </summary>
public class OperateLogInput : VisitLogInput
{
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,150 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using Furion.FriendlyException;
using Microsoft.Extensions.DependencyInjection;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc cref="IRelationService"/>
public class RelationService : DbRepository<SysRelation>, IRelationService
{
private readonly IServiceScope _serviceScope;
public RelationService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScope = serviceScopeFactory.CreateScope();
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationByCategoryAsync(string category)
{
//先从Cache拿需要获取新的对象避免操作导致缓存中对象改变
var sysRelations = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysRelation>>(CacheConst.CACHE_SYSRELATION + category, true);
if (sysRelations == null)
{
//cache没有就去数据库拿
sysRelations = await GetListAsync(it => it.Category == category);
if (sysRelations.Count > 0)
{
//插入Cache
_serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSRELATION + category, sysRelations, true);
}
}
return sysRelations;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByObjectIdAndCategoryAsync(long objectId, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => it.ObjectId == objectId).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByObjectIdListAndCategoryAsync(List<long> objectIds, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => objectIds.Contains(it.ObjectId)).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByTargetIdAndCategoryAsync(string targetId, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => it.TargetId == targetId).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<List<SysRelation>> GetRelationListByTargetIdListAndCategoryAsync(List<string> targetIds, string category)
{
var sysRelations = await GetRelationByCategoryAsync(category);
var result = sysRelations.Where(it => targetIds.Contains(it.TargetId)).ToList();//获取关系集合
return result;
}
/// <inheritdoc/>
public async Task<SysRelation> GetWorkbenchAsync(long userId)
{
var sysRelations = await GetRelationByCategoryAsync(CateGoryConst.Relation_SYS_USER_WORKBENCH_DATA);
var result = sysRelations.FirstOrDefault(it => it.ObjectId == userId);//获取个人工作台
return result;
}
/// <inheritdoc/>
public void RefreshCache(string category)
{
_serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRELATION + category);//删除cache
}
/// <inheritdoc/>
public async Task SaveRelationAsync(string category, long objectId, string targetId, string extJson, bool clear, bool refreshCache = true)
{
var sysRelation = new SysRelation
{
ObjectId = objectId,
TargetId = targetId,
Category = category,
ExtJson = extJson
};
//事务
var result = await itenant.UseTranAsync(async () =>
{
if (clear)
await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
await InsertAsync(sysRelation);//添加新的
});
if (result.IsSuccess)//如果成功了
{
if (refreshCache)
RefreshCache(category);
}
else
{
//写日志
throw Oops.Oh(result.ErrorMessage);
}
}
/// <inheritdoc/>
public async Task SaveRelationBatchAsync(string category, long objectId, List<string> targetIds, List<string> extJsons, bool clear)
{
var sysRelations = new List<SysRelation>();//要添加的列表
for (int i = 0; i < targetIds.Count; i++)
{
sysRelations.Add(new SysRelation
{
ObjectId = objectId,
TargetId = targetIds[i],
Category = category,
ExtJson = extJsons?[i]
});
}
var result = await itenant.UseTranAsync(async () =>
{
if (clear)
await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
await InsertRangeAsync(sysRelations);//添加新的
});
if (result.IsSuccess)//如果成功了
{
RefreshCache(category);
}
else
{
throw Oops.Oh(result.ErrorMessage);
}
}
}

View File

@@ -0,0 +1,72 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色按钮资源
/// </summary>
public class RoleGrantResourceButton
{
/// <summary>
/// 按钮id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 标题
/// </summary>
public string Title { get; set; }
}
/// <summary>
/// 授权菜单类
/// </summary>
public class RoleGrantResourceMenu
{
/// <summary>
/// 菜单下按钮集合
/// </summary>
public List<RoleGrantResourceButton> Button { get; set; } = new List<RoleGrantResourceButton>();
/// <summary>
/// 菜单id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 父id
/// </summary>
public long ParentId { get; set; }
/// <summary>
/// 父名称
/// </summary>
public string ParentName { get; set; }
/// <summary>
/// 菜单名称
/// </summary>
public string Title { get; set; }
}
/// <summary>
/// Blazor Server的组件路由内容
/// </summary>
public class PermissionTreeSelector
{
/// <summary>
/// 路由名称
/// </summary>
public string ApiRoute { get; set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,82 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色授权资源参数
/// </summary>
public class GrantResourceInput : RoleOwnResourceOutput
{
/// <summary>
/// 授权资源信息
/// </summary>
[Required(ErrorMessage = "授权资源信息表不能为空")]
public override List<RelationRoleResuorce> GrantInfoList { get; set; }
/// <summary>
/// 角色Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 角色授权用户参数
/// </summary>
public class GrantUserInput
{
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "GrantInfoList不能为空")]
public List<long> GrantInfoList { get; set; }
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long? Id { get; set; }
}
/// <summary>
/// 角色添加参数
/// </summary>
public class RoleAddInput : SysRole
{
/// <summary>
/// 名称
/// </summary>
[Required(ErrorMessage = "Name不能为空")]
public override string Name { get; set; }
}
/// <summary>
/// 角色编辑参数
/// </summary>
public class RoleEditInput : RoleAddInput
{
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 角色查询参数
/// </summary>
public class RolePageInput : BasePageInput
{
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,62 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话输出
/// </summary>
public class SessionOutput : PrimaryKeyEntity
{
/// <summary>
/// 账号
///</summary>
[Description("账号")]
[DataTable(Order = 1, IsShow = true, Sortable = true)]
public virtual string Account { get; set; }
/// <summary>
/// 最新登录ip
///</summary>
[Description("最新登录ip")]
[DataTable(Order = 3, IsShow = true, Sortable = true)]
public string LatestLoginIp { get; set; }
/// <summary>
/// 最新登录时间
///</summary>
[Description("最新登录时间")]
[DataTable(Order = 4, IsShow = true, Sortable = true)]
public DateTime? LatestLoginTime { get; set; }
/// <summary>
/// 在线状态
/// </summary>
[Description("在线状态")]
[DataTable(Order = 2, IsShow = true, Sortable = true)]
public bool OnlineStatus { get; set; }
/// <summary>
/// 令牌数量
/// </summary>
[Description("令牌数量")]
[DataTable(Order = 5, IsShow = true, Sortable = false)]
public int VerificatCount { get; set; }
/// <summary>
/// 令牌信息集合
/// </summary>
[Description("令牌列表")]
public List<VerificatInfo> VerificatSignList { get; set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,67 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 单页输入参数
/// </summary>
public class SpaAddInput : SysResource
{
/// <summary>
/// 路径
/// </summary>
[Required(ErrorMessage = "Component不能为空")]
public override string Component { get; set; }
/// <summary>
/// 图标
/// </summary>
[Required(ErrorMessage = "Icon不能为空")]
public override string Icon { get; set; }
/// <summary>
/// 菜单类型
/// </summary>
public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
/// <summary>
/// 标题
/// </summary>
[Required(ErrorMessage = "Title不能为空")]
public override string Title { get; set; }
}
/// <summary>
/// 单页输入参数
/// </summary>
public class SpaPageInput : BasePageInput
{
/// <summary>
/// 跳转类型
/// </summary>
public TargetTypeEnum TargetType { get; set; }
}
/// <summary>
/// 单页修改参数
/// </summary>
public class SpaEditInput : SpaAddInput
{
/// <summary>
/// ID
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}

View File

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

View File

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

View File

@@ -0,0 +1,76 @@
#region copyright
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://diego2098.gitee.io/thingsgateway-docs/
// QQ群605534569
//------------------------------------------------------------------------------
#endregion
using SqlSugar;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 添加用户参数
/// </summary>
public class UserAddInput : SysUser
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
}
/// <summary>
/// 编辑用户参数
/// </summary>
public class UserEditInput : UserAddInput
{
/// <summary>
/// 账号
/// </summary>
[Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
public override string Account { get; set; }
/// <summary>
/// Id
/// </summary>
[MinValue(1, ErrorMessage = "Id不能为空")]
public override long Id { get; set; }
}
/// <summary>
/// 用户分页查询参数
/// </summary>
public class UserPageInput : BasePageInput
{
/// <summary>
/// 动态查询条件
/// </summary>
public Expressionable<SysUser> Expression { get; set; }
}
/// <summary>
/// 用户授权角色参数
/// </summary>
public class UserGrantRoleInput
{
/// <summary>
/// Id
/// </summary>
[Required(ErrorMessage = "Id不能为空")]
public long Id { get; set; }
/// <summary>
/// 授权权限信息
/// </summary>
[Required(ErrorMessage = "RoleIdList不能为空")]
public List<long> RoleIdList { get; set; }
}

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