Compare commits

...

753 Commits

Author SHA1 Message Date
2248356998 qq.com
c0337e2b19 build: 10.11.105
feat: json添加AllowNamedFloatingPointLiterals
2025-10-15 11:16:18 +08:00
2248356998 qq.com
8a95f48f5a 适配 net10 rc2 2025-10-15 10:02:11 +08:00
2248356998 qq.com
14f3c31265 build: 10.11.102 2025-10-14 22:22:06 +08:00
2248356998 qq.com
1bad65378f build: 10.11.101 2025-10-14 11:46:26 +08:00
2248356998 qq.com
db3affc67e build: 10.11.100
feat(VariablePage): 优化UI性能
2025-10-14 10:18:09 +08:00
2248356998 qq.com
5ee8b50a92 build: 10.11.99 2025-10-13 22:11:05 +08:00
2248356998 qq.com
301beda2a2 build: 10.11.98 2025-10-13 21:10:39 +08:00
2248356998 qq.com
628b51a353 build: 10.11.97 2025-10-12 15:15:42 +08:00
2248356998 qq.com
f03445bc83 更新依赖 2025-10-12 00:25:57 +08:00
2248356998 qq.com
55a2ff5487 build: 10.11.96 2025-10-11 13:38:28 +08:00
2248356998 qq.com
0fef7dcf3b 更新依赖 2025-10-10 21:41:24 +08:00
2248356998 qq.com
19d9702606 fix: sqlite 批量更新 2025-10-10 14:19:43 +08:00
2248356998 qq.com
a8a9774932 fix: orm 序列化 2025-10-10 12:26:35 +08:00
2248356998 qq.com
aad0f0e8c3 fix: orm批量插入 2025-10-10 11:19:14 +08:00
2248356998 qq.com
e74eae50a7 build: 10.11.87 2025-10-09 20:58:16 +08:00
2248356998 qq.com
3b16d7019f feat: 优化orm 批量插入 2025-10-09 20:57:18 +08:00
2248356998 qq.com
3e038028c2 feat: 优化orm 批量插入 2025-10-09 19:19:18 +08:00
2248356998 qq.com
b1d8041f7e feat: 优化orm BulkCopy 2025-10-09 19:05:33 +08:00
2248356998 qq.com
53a98b26cd fix: 数据库插件保留天数逻辑错误 2025-10-09 08:52:02 +08:00
2248356998 qq.com
42c740fa1b 更新依赖 2025-10-07 22:25:38 +08:00
2248356998 qq.com
556819c90c 10.11.83 2025-09-30 15:25:15 +08:00
2248356998 qq.com
2522333a9c 更新依赖 2025-09-29 23:16:34 +08:00
2248356998 qq.com
bd4ce7c09b 适配net10 2025-09-29 17:47:24 +08:00
2248356998 qq.com
156ed88bd6 10.11.80 2025-09-29 17:09:12 +08:00
2248356998 qq.com
2416226eb0 10.11.78 2025-09-28 17:01:19 +08:00
2248356998 qq.com
976323a716 适配net10 2025-09-28 13:06:39 +08:00
2248356998 qq.com
3c9e397403 10.11.76 2025-09-28 00:33:56 +08:00
2248356998 qq.com
79406ad4a0 更新依赖 2025-09-27 23:59:38 +08:00
2248356998 qq.com
20c44f10ca 10.11.73 2025-09-27 20:18:56 +08:00
2248356998 qq.com
31d6b2a9e6 添加FastMapper 2025-09-26 15:24:37 +08:00
2248356998 qq.com
68e5a9c546 build: 10.11.71 2025-09-26 11:51:05 +08:00
2248356998 qq.com
b2ea9f99b9 适配net10 2025-09-26 10:58:57 +08:00
2248356998 qq.com
6d7d0e468a fix: opcua连接状态 2025-09-24 11:03:23 +08:00
2248356998 qq.com
ff1f632de2 build: 10.11.67 sqldb插件 2025-09-23 22:38:05 +08:00
2248356998 qq.com
fc3d7015ee sqldb插件 清理数据cron表达式错误 2025-09-23 22:36:45 +08:00
2248356998 qq.com
40c5acb522 调整UI 2025-09-23 13:41:20 +08:00
2248356998 qq.com
f9cc1cbb05 10.11.65 2025-09-23 12:02:17 +08:00
2248356998 qq.com
cf6e8b58f0 更新内存变量 2025-09-23 01:22:02 +08:00
2248356998 qq.com
615e3bb24c 修改 脚本调试项目 依赖项 2025-09-22 14:58:03 +08:00
2248356998 qq.com
4a7534b210 添加 脚本调试项目 2025-09-22 10:36:28 +08:00
2248356998 qq.com
58e099cb93 10.11.58 2025-09-21 20:53:50 +08:00
2248356998 qq.com
a94a9c953c 10.11.57 2025-09-19 14:32:34 +08:00
Diego
35e1ffa3e9 添加文件 2025-09-18 17:54:29 +08:00
Diego
4921642151 10.11.56 2025-09-18 17:15:57 +08:00
2248356998 qq.com
d71ee29da8 10.11.54 2025-09-17 20:50:41 +08:00
2248356998 qq.com
901aa2d59f 10.11.52 2025-09-17 11:07:45 +08:00
2248356998 qq.com
764957c014 10.11.51 2025-09-16 22:19:01 +08:00
Diego
0e3898218b 10.11.44 2025-09-16 19:13:19 +08:00
Diego
61f13cef3c 10.11.43 2025-09-15 17:35:49 +08:00
2248356998 qq.com
0b663d9e01 fix: modbusRtu 0x10 2025-09-14 11:18:41 +08:00
2248356998 qq.com
6c95c6209f 10.11.41 2025-09-13 14:22:13 +08:00
2248356998 qq.com
4d223d2622 10.11.40 2025-09-13 13:00:52 +08:00
yunqi
e8d7e91b64 !74 modify dlt645_2007 buildcmd
* modify dlt645_2007 buildcmd
2025-09-13 04:59:10 +00:00
2248356998 qq.com
8175f541ec 更新基准测试 2025-09-12 23:53:36 +08:00
2248356998 qq.com
0adbdb926b 更新基准测试 2025-09-12 23:49:18 +08:00
Diego
42adee9980 更新依赖 2025-09-11 22:22:39 +08:00
2248356998 qq.com
427a7404bc 更新UI 2025-09-11 19:05:14 +08:00
2248356998 qq.com
3658199e0a 10.11.37 2025-09-11 18:30:49 +08:00
Diego
82eedee50a 更新依赖 2025-09-11 18:12:40 +08:00
2248356998 qq.com
6a18fc3e06 10.11.36 2025-09-10 12:59:54 +08:00
2248356998 qq.com
c37e314ed6 更新依赖 2025-09-09 21:53:39 +08:00
2248356998 qq.com
a937a85d90 添加项目 2025-09-08 21:51:37 +08:00
2248356998 qq.com
35dd4ae9d3 更新依赖 2025-09-08 21:27:49 +08:00
2248356998 qq.com
0b829ac85c 10.11.33 2025-09-08 21:16:37 +08:00
2248356998 qq.com
aa247422d2 10.11.31 2025-09-08 17:56:19 +08:00
2248356998 qq.com
2e00e8c135 更新依赖 2025-09-08 08:46:52 +08:00
2248356998 qq.com
34dd2cf0a7 更新依赖 2025-09-07 21:52:41 +08:00
2248356998 qq.com
8404e20c5e build: 10.11.27
refactor: 更新依赖
2025-09-06 23:34:32 +08:00
2248356998 qq.com
662aa162e9 fix: sugar无实体curd 2025-09-05 13:16:18 +08:00
2248356998 qq.com
5927738c32 10.11.25 2025-09-05 11:30:33 +08:00
2248356998 qq.com
3c9f97a5c3 10.11.24 2025-09-04 22:39:21 +08:00
2248356998 qq.com
179ca0aa0e build: 10.11.23
feat: 增加部分demo
feat: 增加管理软件桌面端
2025-09-03 17:40:19 +08:00
Diego
fc09a52da1 build: 10.11.22
refactor:性能优化
2025-09-02 11:13:54 +08:00
Diego
5436b91c89 10.11.21 2025-09-01 18:40:12 +08:00
Diego
d1c46f51a6 添加api接口 2025-09-01 16:00:53 +08:00
Diego
4539d8d198 fix: 字符串反转功能失效 2025-09-01 11:52:03 +08:00
2248356998 qq.com
9ea9529a5f 10.11.19 2025-08-29 15:43:02 +08:00
2248356998 qq.com
4e6be23aac 10.11.17 2025-08-28 17:15:08 +08:00
2248356998 qq.com
2fabbd236b 10.11.16 2025-08-28 17:05:00 +08:00
2248356998 qq.com
163a66530e fix: dlt645校验和 2025-08-28 17:04:27 +08:00
2248356998 qq.com
29073a00c4 fix: dtu连接注册包解析错误 2025-08-28 15:35:43 +08:00
2248356998 qq.com
c6d4d1ecfa fix: dtu连接注册包解析错误 2025-08-28 15:20:00 +08:00
2248356998 qq.com
ba16889cad 10.11.14 2025-08-28 14:21:58 +08:00
2248356998 qq.com
5aaed35b0f 10.11.12 2025-08-28 01:42:59 +08:00
Diego
df067c91eb 10.11.12 2025-08-28 01:39:39 +08:00
2248356998 qq.com
2078b4a60b 补充忽略的文件 2025-08-26 08:54:09 +08:00
2248356998 qq.com
20a2e3ff8e 示例 2025-08-25 20:39:26 +08:00
Diego
61a973b1b5 !73 10.11.10 2025-08-25 12:21:12 +00:00
2248356998 qq.com
cbd72e2081 添加文本日志配置json文件 2025-08-22 16:17:25 +08:00
2248356998 qq.com
4e0377b20c 添加文本日志配置json文件 2025-08-22 16:15:54 +08:00
2248356998 qq.com
fd318d3cdc 移除警告项 2025-08-22 16:12:14 +08:00
2248356998 qq.com
515bdb9700 10.11.7 2025-08-21 23:35:12 +08:00
2248356998 qq.com
46c1780017 10.11.6 2025-08-21 22:40:53 +08:00
2248356998 qq.com
fe78a4c3ca 更新依赖库 2025-08-21 22:05:30 +08:00
2248356998 qq.com
2d7effadf9 10.11.4 2025-08-21 21:13:18 +08:00
Diego
346c560f8b !72 更新依赖项 2025-08-21 11:46:58 +00:00
Diego
8e3bd89f61 修改编译项 2025-08-18 17:30:34 +08:00
Diego
6da142d080 10.10.23 2025-08-18 17:03:40 +08:00
2248356998 qq.com
ff7d029e6f 更新依赖 2025-08-14 20:25:00 +08:00
2248356998 qq.com
21b4695683 10.10.19 2025-08-14 05:47:54 +08:00
Diego
02ad494a26 !71 适配远程客户端 2025-08-12 16:00:53 +00:00
2248356998 qq.com
280366e1b2 添加写优先选项,默认false 2025-08-09 13:07:08 +08:00
2248356998 qq.com
6660ce3e34 适配远程管理客户端 2025-08-08 18:01:24 +08:00
2248356998 qq.com
7499162c1a 适配远程管理客户端 2025-08-08 02:51:42 +08:00
2248356998 qq.com
40208a5cd6 适配远程网关管理客户端 2025-08-08 02:16:05 +08:00
2248356998 qq.com
fa347f4f68 修改报警事件时间字段,增加变量表报警类 2025-08-07 19:19:36 +08:00
Diego
d7df6fc605 10.10.12 2025-08-07 11:24:57 +08:00
2248356998 qq.com
eb4bb2fd48 10.10.11 2025-08-07 10:19:28 +08:00
2248356998 qq.com
faa9858974 10.10.11 2025-08-07 10:18:22 +08:00
2248356998 qq.com
1b3d2dda49 QuestDbRestAPI 2025-08-07 10:11:37 +08:00
Diego
a8a9453611 !70 fix: questdb restapi多实例 2025-08-07 01:58:32 +00:00
2248356998 qq.com
e84f42ce14 10.10.10 2025-08-06 21:42:46 +08:00
2248356998 qq.com
6f814cf6b8 更新questdb restapi启用字段 2025-08-06 21:42:23 +08:00
2248356998 qq.com
e36432e4e9 10.10.9 2025-08-06 19:33:30 +08:00
Diego
ebd71e807b !69 更新依赖 2025-08-06 11:25:31 +00:00
Diego
34000d8d7d !68 10.10.7 2025-08-05 09:22:11 +00:00
2248356998 qq.com
e785f6660c 10.10.5 2025-08-01 21:53:47 +08:00
Diego
831c611797 10.10.4 2025-08-01 17:30:37 +08:00
Diego
453817ef86 添加IAsyncDisposable 2025-08-01 16:36:27 +08:00
2248356998 qq.com
8ce0b981c1 no message 2025-08-01 12:55:01 +08:00
2248356998 qq.com
4e5c51b54c 2025-08-01 12:47:21 +08:00
2248356998 qq.com
3cc9d31f28 修改可用内存策略 2025-08-01 12:43:39 +08:00
2248356998 qq.com
10391f869b 支持相对路径创建sqlite 2025-07-31 23:41:58 +08:00
Diego
fba0723a6d 更新依赖 2025-07-31 18:39:46 +08:00
Diego
2db3f78f0c 添加 控制写操作与读操作的比率 的插件配置属性 2025-07-31 18:18:41 +08:00
Diego
badf61fe01 10.9.99 2025-07-31 16:25:47 +08:00
Diego
d74e0952dc 10.9.98 2025-07-31 15:57:33 +08:00
Diego
fb1699ce80 10.9.97 2025-07-31 14:06:47 +08:00
Diego
44adddbcd4 10.9.97 2025-07-31 13:56:49 +08:00
Diego
0eab889452 10.9.97 2025-07-31 13:56:34 +08:00
Diego
e14d39a459 添加 rpc写入 多写日志 2025-07-31 12:52:40 +08:00
2248356998 qq.com
7575264ede 添加变量初始化标记 2025-07-31 00:40:03 +08:00
2248356998 qq.com
3e1a077b96 添加变量初始化标记 2025-07-31 00:30:53 +08:00
2248356998 qq.com
a921cb8400 refactor: 连接时判断setup 2025-07-30 23:27:35 +08:00
2248356998 qq.com
4de7c31ed7 10.9.92 2025-07-30 22:00:23 +08:00
2248356998 qq.com
08326a2cfd fix: 单属性验证异常 2025-07-30 21:11:35 +08:00
Diego
e045de5acb !67 更新依赖 2025-07-30 10:01:20 +00:00
2248356998 qq.com
d3bef31aa6 10.9.70 2025-07-30 04:32:14 +08:00
2248356998 qq.com
347d4d6e5d 10.9.69 2025-07-25 22:21:34 +08:00
2248356998 qq.com
b006a066e1 10.9.69 2025-07-25 22:14:06 +08:00
Diego
e331bc5d3c 10.9.68 2025-07-25 20:30:48 +08:00
Diego
2e81806231 优化 2025-07-25 20:20:35 +08:00
Diego
37b48cf221 BindField 2025-07-24 20:03:53 +08:00
Diego
5997487434 修改参数 2025-07-24 19:46:30 +08:00
Diego
4ce843182f build: 10.9.67
feat: 报警恢复增加延时功能
refactor: 移除通讯任务时,设置设备、变量离线状态

fix: 罗克韦尔PLC通讯超时错误
2025-07-24 13:25:55 +08:00
2248356998 qq.com
a4aa000cf0 10.9.66 2025-07-23 19:11:39 +08:00
Diego
01aa6ca066 10.9.65 2025-07-23 16:34:02 +08:00
Diego
10a0280c4a 10.9.64 2025-07-22 15:55:49 +08:00
Diego
3b8b00c783 变量类验证提示 2025-07-22 11:03:48 +08:00
Diego
232ee5d5d4 10.9.63 2025-07-22 01:17:12 +08:00
2248356998 qq.com
605830edce 10.9.62 2025-07-20 21:52:59 +08:00
2248356998 qq.com
ca86441e05 orm性能优化 2025-07-20 19:04:25 +08:00
2248356998 qq.com
b30b876c5f 更新依赖 2025-07-20 10:44:20 +08:00
2248356998 qq.com
f95590b4cb 修正拼写错误 2025-07-19 22:36:13 +08:00
2248356998 qq.com
9921130406 更新依赖 2025-07-19 21:38:19 +08:00
2248356998 qq.com
5c47589ca1 更新依赖 2025-07-19 21:31:46 +08:00
2248356998 qq.com
048abfae2e 添加可空标识 2025-07-19 13:35:55 +08:00
Diego
8e35c16edf 恢复restart标记 2025-07-18 17:46:56 +08:00
Diego
143b751213 Revert "build: 10.9.42"
This reverts commit 2cafe745b9.
2025-07-18 14:41:19 +08:00
Diego
2cafe745b9 build: 10.9.42
feat: 优化读写调度
feat: rpc日志添加执行时间字段
2025-07-18 09:48:41 +08:00
Diego
210ac2c122 更新解决方案 2025-07-18 00:02:38 +08:00
Diego
4707ce6d58 新增RemoteWebApp项目 2025-07-17 23:41:11 +08:00
2248356998 qq.com
788a8b670d 10.9.40 2025-07-16 21:18:14 +08:00
2248356998 qq.com
5e4f0057e4 更新依赖库 2025-07-15 21:24:19 +08:00
2248356998 qq.com
2960c13ef1 更新依赖库 2025-07-15 03:56:46 +08:00
2248356998 qq.com
f11b7f7ab4 10.9.37 2025-07-13 22:47:14 +08:00
Diego
9bb9cd7419 10.9.35 2025-07-11 07:14:52 +08:00
Diego
ba008ef8ba 更新.NETVersion 2025-07-09 13:35:42 +08:00
Diego
28b533decf 10.9.34 2025-07-09 13:31:08 +08:00
Diego
9ee638c2f1 更新依赖包 2025-07-08 23:59:24 +08:00
Diego
d90fdbaf35 补充更新nuget包 2025-07-08 18:15:50 +08:00
Diego
b55e3db736 10.9.29 2025-07-08 17:55:37 +08:00
Diego
dbee8496cb opcuaServer支持decimal类型 2025-07-08 16:53:28 +08:00
Diego
044e78bea9 opcuaserver回退修改 2025-07-08 15:52:12 +08:00
Diego
fe79128d90 build: 10.9.26
fix: opcuaserver添加变量属性出现错误,已回退
refactor: 网关监控树节点保持展开状态
refactor: cache插件类日志输出
2025-07-08 14:33:27 +08:00
Diego
34120da008 ToDecimal方法添加异步捕获,防止double类型值超限 2025-07-08 11:21:06 +08:00
Diego
4c62bb0b21 10.9.25 2025-07-08 10:19:01 +08:00
Diego
46b16279c7 build: 10.9.24 2025-07-08 09:41:45 +08:00
2248356998 qq.com
0779efc5dd refactor: 冗余服务代码修正 2025-07-08 02:38:05 +08:00
Diego
8acdb780e8 nuget本地源创建文件夹 2025-07-07 19:07:08 +08:00
Diego
2e310b919e 添加Keys文件夹 2025-07-07 16:54:58 +08:00
Diego
4155c07269 10.9.23 2025-07-07 15:20:39 +08:00
Diego
32fa833736 build: 10.9.22 2025-07-07 15:10:06 +08:00
Diego
2258f08555 build: 10.9.19 2025-07-07 13:44:19 +08:00
2248356998 qq.com
d90b32f165 refactor: 调整冗余服务代码 2025-07-07 00:15:46 +08:00
2248356998 qq.com
1492377322 refactor:调整部分插件父类 2025-07-06 01:33:15 +08:00
Diego
32eefbf545 build: 10.9.18
feat: 网关监控树节点添加右侧常用操作按钮
2025-07-04 13:54:35 +08:00
Diego
a825ca5f6f 网关监控树节点添加右侧常用操作按钮 2025-07-04 12:50:07 +08:00
Diego
e270b0c4f6 no message 2025-07-04 09:01:31 +08:00
Diego
6ae44ccf58 10.9.17 2025-07-04 08:36:37 +08:00
Diego
1aa0df6339 feat: 支持变量低内存导出 2025-07-03 23:24:56 +08:00
Diego
62c3693dbe 导出通道和设备支持IEnumerable低内存 2025-07-03 19:34:51 +08:00
Diego
e4e503c97b build: 添加nuget.config,自定义nuget源,方便本地调试自行编译 2025-07-03 12:43:51 +08:00
2248356998 qq.com
5ec1ee7627 fix: 上次更新导致大小端配置映射错误 2025-07-02 21:51:00 +08:00
Diego
79789388fc fix: opcuaserver动态刷新变量节点会导致新建的动态节点无法刷新订阅 2025-07-02 19:11:11 +08:00
Diego
2c4194ee18 refactor: opcua某些不存在的节点ID不再影响整体订阅,只出现日志提示 2025-07-02 15:09:52 +08:00
Diego
1b2be585af fix: 报警分析错误设置循环 2025-07-02 14:26:25 +08:00
Diego
83736647e7 feat: S7PLC增加WString支持 2025-07-02 12:49:55 +08:00
Diego
b06405717d build: 10.9.9
fix: timerx 池 max值取消
feat: mqttrpc支持脚本
2025-07-02 10:03:50 +08:00
2248356998 qq.com
298a1f2ed4 更新docker文件 2025-07-02 07:32:23 +08:00
Diego
74a47a1983 build: 10.9.8
支持redis缓存
2025-07-01 17:55:03 +08:00
Diego
a48a42abe4 modbusslave 异常捕获 2025-07-01 10:51:10 +08:00
Diego
feb1d0a3c5 feat: 增加后台服务生命周期识别 2025-06-30 10:59:18 +08:00
Diego
92bca824e6 10.9.6 2025-06-29 22:19:53 +08:00
2248356998 qq.com
025c699517 feat: s7增加请求id识别 2025-06-29 22:06:53 +08:00
2248356998 qq.com
53f8fbe4b1 refactor: 变量排序导出 2025-06-28 21:43:06 +08:00
2248356998 qq.com
77bfabc41d feat: 改用Mapperly源生成,代替Mapster 2025-06-28 00:00:43 +08:00
Diego
6427ee6ee0 refactor: 降低sqlite依赖 2025-06-27 14:44:00 +08:00
Diego
4c95997d62 build: 10.9.2
fix: taos connection dispose
refactor: opcua AddSubscriptionAsync 添加延时和重试
2025-06-27 11:16:58 +08:00
Diego
1d82cea40d build: 10.9.1
fix: 任务空错误异常
feat: 数据库插件增加字符串变量表和数值变量表两种情况
2025-06-27 03:02:03 +08:00
Diego
e78799424c 添加日志输出筛选 2025-06-25 17:09:05 +08:00
Diego
1000c8d38f docs: 更新采集插件开发文档 2025-06-25 14:22:58 +08:00
Diego
b2589fc634 build: 10.8.24
fix: 变量离线后再次上线,如果值不变,会导致在线状态不刷新
fix: s7离线恢复时,可能触发多次协议握手导致异常
2025-06-25 11:19:06 +08:00
Diego
c80e57a4e8 build: 10.8.24
fix: 变量离线后再次上线,如果值不变,会导致在线状态不刷新
fix: s7离线恢复时,可能触发多次协议握手导致异常
2025-06-25 11:17:04 +08:00
Diego
6510c3e289 feat: 增加一个变量读写表达式常用转换的友好编辑界面 2025-06-24 16:14:35 +08:00
Diego
920e407d05 恢复规则引擎脚本接口 2025-06-24 10:52:00 +08:00
Diego
7314c8901d fix: 用户编辑框初始刷新职位 2025-06-24 09:15:33 +08:00
Diego
4e9f02b48c 更新依赖包 2025-06-24 09:03:34 +08:00
2248356998 qq.com
9ae7602cb4 配置最大连接数 2025-06-24 00:09:07 +08:00
2248356998 qq.com
aa8aa36aef build: 10.8.19
fix: s7 复用地址对象导致读取异常
feat: 规则引擎node添加内部异常捕获
feat: 变量增加属性: 写入后再次读取检查值是否一致
2025-06-23 21:21:27 +08:00
2248356998 qq.com
0174f7c6f2 更新依赖 2025-06-22 23:05:12 +08:00
2248356998 qq.com
df9e7d6ff1 10.8.17 2025-06-22 21:11:37 +08:00
Diego
b40ca920d3 fix: 变量自动刷新运行态 2025-06-20 16:58:45 +08:00
Diego
5a4b0a0e93 修改插件过期提示 2025-06-20 14:43:13 +08:00
Diego
a879edd68b 10.8.12 2025-06-20 13:52:42 +08:00
Diego
62e0a6ee9d gitee登录按钮隐藏 2025-06-19 17:04:22 +08:00
Diego
765e5564d4 10.8.10 2025-06-19 16:56:04 +08:00
Diego
10eecac19b feat: 优化设备状态逻辑 2025-06-19 11:41:43 +08:00
Diego
59241b8faa fix: opcua插件订阅检查失效 2025-06-19 10:42:54 +08:00
Diego
52b3097f04 10.8.7 2025-06-18 22:04:48 +08:00
Diego
d922296b70 feat: 重构插件任务 2025-06-18 17:11:57 +08:00
Diego
aec91da28b 10.8.2
feat: 优化闭包导致的状态机内存占用,高并发时内存显著下降
fix(sqldb): 定时上传模式时,实时表时效
fix(taos): 初始化失败
2025-06-17 17:09:05 +08:00
Diego
013ff394be 10.8.1 2025-06-16 18:25:55 +08:00
Diego
081e07473d 10.8.0 2025-06-16 18:12:28 +08:00
Diego
d33d900592 增加github oauth登录 2025-06-15 00:17:25 +08:00
Diego
29365c4ef9 修改脚本测试方法 2025-06-14 11:56:31 +08:00
Diego
17a6189089 style: 更新SysResourcePage页面样式 2025-06-13 16:03:28 +08:00
Diego
003b8a3763 10.7.56 2025-06-13 13:35:38 +08:00
Diego
1c7f8b5cab 10.7.55 2025-06-13 09:14:22 +08:00
Diego
b7ff9ffca2 更新解决方案 2025-06-13 09:06:42 +08:00
Diego
9bba9bda76 10.7.54 2025-06-12 23:54:10 +08:00
Diego
ec2fcc75d3 合并代码 2025-06-12 20:21:50 +08:00
Diego
57a4038577 pwa安装提示优化 2025-06-12 10:20:52 +08:00
Diego
b78a76e60f 增加采集设备写入数据的控制台文本日志 2025-06-11 23:49:37 +08:00
Diego
28bd751d44 增加tab右键菜单 2025-06-11 22:57:00 +08:00
Diego
dcba7b9810 10.7.50 2025-06-11 21:50:46 +08:00
Diego
7f1a741ce6 优化sql实时表执行性能 2025-06-11 21:39:14 +08:00
Diego
ca2b17d433 10.7.47 2025-06-11 17:08:19 +08:00
Diego
af589eac10 更新依赖 2025-06-11 15:05:03 +08:00
Diego
573670f1f5 10.7.45 2025-06-11 10:26:48 +08:00
Diego
f3ec85a03d 更新测试 2025-06-10 16:26:14 +08:00
Diego
c94308454f build: 10.7.42
feat:更改后台服务多语言处理方式
2025-06-10 15:49:47 +08:00
Diego
c9c2b2b69d 10.7.39 2025-06-09 16:11:37 +08:00
Diego
40574b776f feat(TDengine): 支持websocket连接,无需安装sdk 2025-06-09 12:38:30 +08:00
Diego
f426c1533d fix: taos插件 not found dll错误 2025-06-09 10:07:58 +08:00
Diego
4a3b5163f1 更新依赖 2025-06-09 09:29:41 +08:00
2248356998 qq.com
3a0946d357 优化单例db对象获取 2025-06-08 21:09:07 +08:00
Diego
9dd21c644c 10.7.35 2025-06-08 15:48:13 +08:00
2248356998 qq.com
6713190a23 日志写入器释放 2025-06-08 15:47:25 +08:00
Diego
9f24533425 10.7.34 2025-06-08 15:38:41 +08:00
2248356998 qq.com
ed1d9374eb 2025-06-08 01:22:41 +08:00
Diego
f14d27129e 10.7.33 2025-06-07 22:30:27 +08:00
2248356998 qq.com
5b04f02fbe Merge branch 'v10' of https://gitee.com/ThingsGateway/ThingsGateway into v10 2025-06-07 22:26:32 +08:00
2248356998 qq.com
227080e332 ddp协议清理旧连接逻辑错误 2025-06-07 22:26:18 +08:00
Diego
1e87482a49 10.7.32 2025-06-07 15:25:38 +08:00
2248356998 qq.com
054d31c3ea 添加自定义插件 2025-06-07 15:14:54 +08:00
Diego
3a068a7b03 更新解决方案 2025-06-07 12:22:05 +08:00
Diego
b12e923c99 10.7.31 2025-06-06 23:47:07 +08:00
Diego
ab33eed8d3 10.7.22 2025-06-06 21:13:58 +08:00
Diego
d930a9a8eb 修改演示站登录ui 2025-06-05 08:40:25 +08:00
Diego
af381fce12 插件调试增加双击显示 2025-06-04 09:45:02 +08:00
Diego
b64ac0539e 更新依赖 2025-06-03 22:27:28 +08:00
Diego
541c60b363 10.7.17 2025-06-02 19:45:56 +08:00
Diego
824e95f7cb 10.7.16 2025-06-02 19:44:30 +08:00
youthalan
38f7850196 !66 修复缺少引用编译错误
* 修复缺少引用编译错误
2025-05-31 06:35:47 +00:00
Diego
bef9de88e2 oauth增加scope参数 2025-05-31 02:17:09 +08:00
Diego
48cd5e7c7f 增加演示站的gitee授权 2025-05-31 00:43:43 +08:00
Diego
3b44fda51c 修复hybrid程序 2025-05-30 13:48:03 +08:00
Diego
dbfc9a5bb4 更新授权显示 2025-05-30 11:21:45 +08:00
Diego
1b758aa41a 10.6.37 2025-05-30 10:15:58 +08:00
Diego
43bdc70899 优化文本日志读取 2025-05-30 10:02:51 +08:00
Diego
fadda000a6 添加sqlsugar类 2025-05-29 22:18:33 +08:00
Diego
45a8c91a5a 优化UI刷新 2025-05-29 17:05:06 +08:00
Diego
8e938f18be 去除不必要的控制台日志输出 2025-05-28 16:52:51 +08:00
Diego
ab1b364c54 fix: 同步插件反写空错误 2025-05-28 12:30:16 +08:00
Diego
5ec65b2fb0 10.6.29 2025-05-28 10:47:31 +08:00
Diego
926eced724 10.6.28 2025-05-27 17:20:05 +08:00
Diego
f7f8802272 2025-05-27 13:19:39 +08:00
Diego
c6910dff02 update src/Gateway/ThingsGateway.Gateway.Application/ThingsGateway.Gateway.Application.csproj.
Signed-off-by: Diego <2248356998@qq.com>
2025-05-27 01:37:29 +00:00
Diego
ad299d0dbb 2025-05-27 08:52:06 +08:00
2248356998 qq.com
8b124d1050 日志统计查询性能增强 2025-05-27 00:03:30 +08:00
Diego
ff41080dbd 更新授权类 2025-05-26 19:51:21 +08:00
Diego
0e28606e3d 10.6.23 2025-05-26 18:43:42 +08:00
Diego
6a025ceee5 序列化配置增加nan的情况 2025-05-26 17:41:13 +08:00
Diego
6b2e53d6dc 更新配置 2025-05-26 09:17:59 +08:00
2248356998 qq.com
b989aa5561 10.6.21 2025-05-26 00:05:16 +08:00
2248356998 qq.com
f5b0b7ebd2 fix: s7多写 2025-05-24 14:22:58 +08:00
2248356998 qq.com
16881ae076 fix: s7多写 2025-05-24 14:18:58 +08:00
2248356998 qq.com
af04112656 10.6.20
fix: s7多写
fix: opcuaserver写入数组
2025-05-24 14:11:10 +08:00
Diego
a2863112dc 2025-05-23 15:47:41 +08:00
Diego
f531e4dfc5 10.6.19 2025-05-23 13:16:57 +08:00
Diego
8db9b32ba7 10.6.18 2025-05-23 12:55:21 +08:00
Diego
dd5691cbef 更新依赖 2025-05-22 14:40:56 +08:00
Diego
de48b32af3 优化硬件信息曲线数据 2025-05-21 17:10:28 +08:00
Diego
600b5042a1 更新首页 2025-05-21 16:51:40 +08:00
Diego
aac77029da 修改数据保护密钥持久化位置 2025-05-21 13:15:39 +08:00
Diego
e50205f557 导出excel不再按设备名称和变量名称排序 2025-05-21 10:10:59 +08:00
Diego
e227411d1f fix: taos插件查询错误 2025-05-21 09:34:31 +08:00
2248356998 qq.com
2de0ed793f 调整json序列化内容 2025-05-20 23:21:58 +08:00
Diego
cb0276f273 fix: opcuamaster插件属性UI未正确显示 2025-05-20 15:20:14 +08:00
Diego
562b3f17c9 10.6.11 2025-05-19 23:16:49 +08:00
Diego
0f78f81c1c 10.6.7 2025-05-19 18:43:03 +08:00
Diego
6594937d0a 网关冗余备用站和同步插件支持 变量写入 2025-05-19 18:01:23 +08:00
Diego
64bc6084be 更新sln 2025-05-19 12:23:41 +08:00
Diego
20434d5bd2 10.6.5 2025-05-19 12:13:11 +08:00
Diego
9ecc9380e6 同步更改 2025-05-19 12:12:44 +08:00
Diego
a57c55080b 增加数据同步插件 2025-05-19 12:10:11 +08:00
2248356998 qq.com
b8ca06c6be fix: hybrid运行切换语言错误 2025-05-17 16:32:36 +08:00
Diego
5aff6461a1 fix: opcuaServer 业务设备刷新变量时可能导致内存泄露 2025-05-16 19:26:33 +08:00
Diego
6cf53fefec 优化内存占用 2025-05-16 18:00:36 +08:00
Diego
45132f3503 更新依赖 2025-05-16 10:51:44 +08:00
Diego
f1e78a0e8a 恢复配置json 2025-05-15 12:17:43 +08:00
Diego
0bf28ec275 更新语言资源 2025-05-15 12:17:09 +08:00
Diego
41f8412c97 支持达梦数据库 2025-05-15 12:15:32 +08:00
Diego
c535974362 更新规则引擎示例 2025-05-15 10:44:12 +08:00
Diego
1860c5f215 build:10.6.1 2025-05-15 09:12:33 +08:00
Diego
6d778b2d39 增加initDatabase配置项 2025-05-15 09:08:08 +08:00
Diego
f48b99c259 更新示例 2025-05-14 21:12:51 +08:00
Diego
3c73b93051 更新依赖 2025-05-14 18:52:19 +08:00
Diego
98f3f2d519 添加 `过滤离线变量` 插件属性 2025-05-14 13:01:12 +08:00
Diego
b76b4e8d68 变量表索引删除语句兼容性增强 2025-05-13 19:27:50 +08:00
Diego
07285a7c61 mqtt增加qos属性 2025-05-13 19:27:27 +08:00
Diego
03c0dfef37 增加业务设备日志 2025-05-12 15:26:51 +08:00
Diego
6ef6929c35 优化api权限树 2025-05-12 10:21:41 +08:00
Diego
e3c0c173f0 更新依赖 2025-05-12 08:53:25 +08:00
Diego
7d64e058d4 更新依赖 2025-05-08 16:34:48 +08:00
Diego
e97ee9b64b 10.5.15 2025-05-07 22:08:54 +08:00
Diego
6a03e39eeb 10.5.14 2025-05-07 22:00:31 +08:00
Diego
525ec740b5 添加脚本demo 2025-05-06 11:43:56 +08:00
2248356998 qq.com
b790cf5f4e 更新依赖 2025-05-05 20:25:43 +08:00
Diego
d1248811fd build: 10.5.11 2025-04-30 23:05:36 +08:00
2248356998 qq.com
022d016e8e feat: 添加采集组 2025-04-30 23:04:51 +08:00
Diego
f73245e650 build: 10.5.10 2025-04-30 15:31:39 +08:00
2248356998 qq.com
484461fa05 更新依赖 2025-04-30 15:29:19 +08:00
Diego
7e0b7aff2a feat: sqldb支持数组 2025-04-28 15:52:32 +08:00
Diego
6c450dcb09 feat: sqldb支持数组类型 2025-04-28 15:52:11 +08:00
Diego
227f44283f build: 10.5.8 2025-04-28 15:30:10 +08:00
Diego
74f6e79625 build: 10.5.7
优化opcua变量缓存
修复cron表达式间隔
2025-04-27 16:35:58 +08:00
Diego
cec43e2ce8 feat: 防呆设计,强制设置通道的最大并发数 2025-04-27 10:13:35 +08:00
Diego
7553b258bb build: 10.5.5 2025-04-26 17:46:31 +08:00
Diego
8bdbdc117e 支持bind链路设置多个通道 2025-04-26 17:10:46 +08:00
Diego
0e206be296 更新依赖 2025-04-26 16:17:02 +08:00
Diego
00b7353433 兼容性增强 2025-04-26 15:53:20 +08:00
Diego
44e7a83593 更新依赖 2025-04-26 15:02:57 +08:00
Diego
dd68d555d4 feat: 支持字节数组上传 2025-04-24 10:25:36 +08:00
2248356998 qq.com
0456296103 更新依赖包 2025-04-23 23:07:27 +08:00
Diego
a1b66277ff nuget 2025-04-23 15:28:53 +08:00
Diego
50758b79bc 更新依赖包 2025-04-23 11:48:45 +08:00
Diego
06a1f902ad 修改Startup方法名称 2025-04-23 10:56:03 +08:00
Diego
58f8b23b7c build:10.5.1
fix: 反向代理正确获取客户端IP

refactor: 添加demo站点域名
2025-04-22 11:45:14 +08:00
Diego
9e7c348b15 添加大数据写入方法 2025-04-21 23:05:10 +08:00
Diego
5f5ff8b43b 删除实体部分特性 2025-04-21 17:53:33 +08:00
Diego
f626b4e5fc sql插件增加分表模式 属性 2025-04-21 11:00:05 +08:00
2248356998 qq.com
bc23200e66 更新依赖 2025-04-20 20:32:41 +08:00
Diego
95ab59fd5a build: 10.4.21 2025-04-18 11:27:48 +08:00
Diego
0bbee003b0 app.json 2025-04-18 09:21:31 +08:00
Diego
d20cb7a928 兼容10.4.8以下数据库 2025-04-17 09:41:15 +08:00
Diego
952c134aba build: 10.4.19 2025-04-16 09:08:35 +08:00
2248356998 qq.com
5533d980e3 删除不必要的代码 2025-04-15 21:14:35 +08:00
2248356998 qq.com
aa0213818f 添加规则引擎日志清理任务 2025-04-15 20:59:01 +08:00
2248356998 qq.com
af88ffd57c 添加语言资源 2025-04-15 20:07:44 +08:00
2248356998 qq.com
87447b1c2a 传输model添加runtimetype 2025-04-15 20:01:21 +08:00
Diego
384a12880b 网关表格按钮权限 2025-04-15 10:13:37 +08:00
Diego
791f91f377 build: 10.4.18 2025-04-14 15:28:35 +08:00
Diego
4f8b405cc3 build:10.4.17 2025-04-13 13:03:39 +08:00
2248356998 qq.com
e1b7af201a 修改表单映射 2025-04-13 12:09:12 +08:00
2248356998 qq.com
a04b7817a9 refactor: 表格编辑框 model映射 2025-04-13 09:56:11 +08:00
2248356998 qq.com
21bf83aec7 edit css 2025-04-12 14:57:19 +08:00
2248356998 qq.com
d060d5179d build: 10.4.16 2025-04-12 12:24:05 +08:00
2248356998 qq.com
e73dd58e46 fix: 初始连接时,适配器空错误 2025-04-11 23:24:52 +08:00
Diego
a34e4a6942 添加api 2025-04-11 20:21:35 +08:00
Diego
a7ee6d78b5 fix: mqttcollect 重载变量寄存器 2025-04-11 11:01:29 +08:00
Diego
0a6651ac11 build: 10.4.14 2025-04-11 10:30:18 +08:00
Diego
fa25053c13 fix: 宏电ddp协议 tcpudp差异 2025-04-11 10:23:52 +08:00
Diego
61dd4edeb5 更新依赖 2025-04-09 18:28:25 +08:00
Diego
dbaeff302b build: 10.4.13
fix: 通道,设备名称为中文时,日志路径错误
2025-04-09 17:22:46 +08:00
Diego
6cbe7ebcf2 fix: 复制通道 2025-04-09 13:32:04 +08:00
youthalan
2826e08c8c !64 -注册授权页面显示 2025-04-08 14:36:22 +00:00
Diego
e30e337efa refactor: 删除重复的注册授权页面 2025-04-08 10:44:54 +08:00
Diego
39788e0357 refactor: 删除重复的注册授权页面 2025-04-08 10:35:37 +08:00
Diego
c47363df71 build: 10.4.11 2025-04-08 10:20:33 +08:00
Diego
4199453038 refactor: 删除logenable 2025-04-08 09:44:08 +08:00
Diego
db991081c8 build: 10.4.10 2025-04-08 00:53:41 +08:00
Diego
24f946a727 refactor: 组件属性删除 2025-04-08 00:53:22 +08:00
Diego
772dae047a refactor: 删除logenable字段 2025-04-08 00:37:41 +08:00
Diego
020f440fdd !63 build: 10.4.8
* 10.4.8
* 优化ui
* 调整规则引擎
2025-04-07 15:54:03 +00:00
Diego
c88fc5ec09 !62 build: 10.4.7
* build: 10.4.7
* 格式化
* Merge branch 'v10' into v10-dev
* Merge branch 'v10' of gitee.com:ThingsGateway/ThingsGateway into v10-dev
* 修改通道显示字符串
* fix: ddp协议
* 更新依赖
* 10.4.6
* 通道 本地主机地址和远程主机地址 文本修改
* 注册页面位置更改
* 修复导出设备失败,注册页面位置更改
* 更新依赖
* 格式化
* 更新依赖
* 删除会话管理字段
* logo size
* 更新依赖
2025-04-07 07:37:17 +00:00
Diego
475849fbef !61 fix: ddp协议
* Merge branch 'v10' of gitee.com:ThingsGateway/ThingsGateway into v10-dev
* 修改通道显示字符串
* fix: ddp协议
* 更新依赖
* 10.4.6
* 通道 本地主机地址和远程主机地址 文本修改
* 注册页面位置更改
* 修复导出设备失败,注册页面位置更改
* 更新依赖
* 格式化
* 更新依赖
* 删除会话管理字段
* logo size
* 更新依赖
2025-04-07 05:01:56 +00:00
Diego
1d63228fb2 !60 build: 10.4.6
fix: 修复导出设备失败,注册页面位置更改

refactor:通道 本地主机地址和远程主机地址 文本修改
refactor:注册页面位置更改
refactor:更新依赖包
refactor:删除会话管理字段
2025-04-06 12:28:24 +00:00
Diego
c918c0c010 更新authorizel类库 2025-04-03 02:01:08 +08:00
Diego
1028179983 build: 10.4.4 2025-04-03 01:27:43 +08:00
Diego
686f61ff90 refactor: docker镜像基础改为net8 2025-04-02 21:38:36 +08:00
Diego
ae43fadca3 更新依赖包 2025-04-02 20:40:11 +08:00
Diego
1bf05780b3 build: 10.4.3
调整pro授权密钥方式
2025-04-02 18:49:51 +08:00
Diego
3e30daa1d3 refactor: 修改文件名称 2025-04-02 12:37:06 +08:00
Diego
54ef1233b1 refactor: 重启通道时,清空内存池 2025-04-02 10:56:24 +08:00
Diego
4413633de7 build: 10.3.1 2025-04-01 22:58:06 +08:00
2248356998 qq.com
c76e20d146 dispose 2025-04-01 22:56:58 +08:00
Diego
36d53b7ce4 build: 10.2.26 2025-04-01 22:45:58 +08:00
2248356998 qq.com
0b0bce2761 fix: 设备冗余检测事件未释放 2025-04-01 22:44:46 +08:00
Diego
d47a9be03c build: 10.2.25 2025-04-01 22:22:58 +08:00
2248356998 qq.com
bc32d7ae7a refacotr: 插件释放时显式清理较大内存资源 2025-04-01 22:18:39 +08:00
Diego
e6747a2c2c build: 10.2.24
fix: ui page dispose方法未自动执行,可能导致内存泄露
2025-04-01 14:53:10 +08:00
Diego
3fb48cb64e refactor: 报警恢复时报警文本不清空 2025-04-01 11:34:41 +08:00
Diego
381db1cf53 nuget构建 2025-04-01 11:15:23 +08:00
Diego
d3939aeb9d build: 10.2.22
更新依赖项
2025-04-01 11:05:32 +08:00
Diego
5956f90557 Merge branch 'v10' of https://gitee.com/ThingsGateway/ThingsGateway into v10 2025-04-01 10:57:44 +08:00
Diego
7f54ecfe5d build: 10.2.21
feat: 增加汇川plc插件
2025-04-01 10:57:42 +08:00
Diego
e7bda99c8b build: 10.2.21
feat: 增加汇川plc插件
2025-04-01 10:57:06 +08:00
2248356998 qq.com
ea7ab5a2bf refactor: 插件停止方式改为token取消委托 2025-03-31 19:26:11 +08:00
Diego
ee566d199a 规则引擎rpc日志 增加 源名称 2025-03-31 16:19:43 +08:00
Diego
56eca454b1 更新usheet依赖项 2025-03-31 15:26:53 +08:00
Diego
d057974c3e build:10.2.20 2025-03-31 12:39:35 +08:00
Argo Zhang
9514e7fdea 更新样式适配紧凑模式下按钮大小 (#25) 2025-03-31 12:29:56 +08:00
Diego
e546113873 refactor: 添加规则名称 2025-03-31 12:14:10 +08:00
Diego
60f1139355 feat: 增加pro插件标识 2025-03-31 10:05:37 +08:00
Diego
1b18b59054 build: 10.2.18
更新依赖库
2025-03-30 16:39:10 +08:00
Diego
afec0187e9 build: 10.2.17 2025-03-29 21:24:12 +08:00
2248356998 qq.com
d0d7e1ccf8 feat: 优化ui刷新性能 2025-03-29 21:21:51 +08:00
Diego
d72376b4b7 build: 10.2.16 2025-03-29 19:00:00 +08:00
2248356998 qq.com
3be60cb305 refactor: opcua加载复杂类型添加条件 2025-03-29 18:51:57 +08:00
Diego
75b1f351a1 build: 10.2.15 2025-03-29 12:25:51 +08:00
2248356998 qq.com
07185f8018 refactor: 多通道设备日志 2025-03-29 12:23:53 +08:00
Diego
2f081b62a7 build: 10.2.14 2025-03-29 11:42:18 +08:00
2248356998 qq.com
bbce2a11e1 refactor:opcua检查订阅 2025-03-29 11:38:17 +08:00
2248356998 qq.com
96ab609157 删除注释 2025-03-29 11:23:49 +08:00
2248356998 qq.com
d961d56a35 refactor: opcua日志内容 2025-03-29 00:20:38 +08:00
Diego
82a5335a92 build: 10.2.13 2025-03-28 23:32:16 +08:00
2248356998 qq.com
729a23949c refactor: opcua增加质量戳bad日志 2025-03-28 23:29:12 +08:00
2248356998 qq.com
a1b0419d7a refactor: 在线excel 2025-03-28 23:12:00 +08:00
2248356998 qq.com
7a23015b9d refactor: 采集插件基类CollectBase更名CollectFoundationBase,CollectBase适用与与touchsocket无关的采集插件 2025-03-28 22:58:19 +08:00
Diego
9cbe0dbae1 feat: 增加tab右键菜单
refactor: mqtt协议版本增加默认值3.11
2025-03-28 17:15:45 +08:00
Diego
4f139b7d03 更新readme 2025-03-28 11:16:28 +08:00
Diego
1e925e2560 refactor: 自定义cookie名称 2025-03-28 11:14:01 +08:00
Diego
a42e1e3af3 feat: mqtt插件添加mqtt版本选择
fix: s7地址格式解析db块逻辑错误
2025-03-28 10:45:50 +08:00
Diego
799caec7ee refactor: 删除没有使用的代码 2025-03-28 10:20:35 +08:00
Diego
dd7d30498f feat: 添加变量复制按钮 2025-03-28 09:06:54 +08:00
Diego
e9b1720e98 refactor: DataHandleAdapter 2025-03-27 17:46:53 +08:00
Diego
5f41371bdb refactor: 更新配置 2025-03-27 17:09:13 +08:00
Diego
5226bfae20 build: 10.2.9 2025-03-27 15:06:02 +08:00
Diego
d1eab168b7 build: 10.2.8 2025-03-26 17:47:24 +08:00
Diego
8760518c10 build: 10.2.7 2025-03-26 09:19:46 +08:00
Diego
6719a352c2 hybrid程序startup 2025-03-25 17:14:37 +08:00
Diego
8449b6c6b8 refactor: miniexcel版本回退 2025-03-25 15:27:05 +08:00
Diego
0550eaf465 feat: 规则引擎 自定义脚本 增加主动释放方法 2025-03-25 10:55:25 +08:00
Diego
bdc9eb44ec feat: 规则引擎 自定义执行脚本支持 `IDisposable` 接口
refactor: 脚本内日志对象统一命名为 ``Logger``
2025-03-25 10:46:03 +08:00
Diego
37b99cb1b0 feat: 增加脚本提示 2025-03-25 03:22:37 +08:00
Diego
492381e4fc refactor: 修改变量数量限制逻辑 2025-03-24 14:49:41 +08:00
Diego
27d65dd799 refactor: 变量名称不再限制 不包含. 符号 2025-03-24 11:40:27 +08:00
Diego
434724310c build: 10.2.1
feat: 新增 配置 通道、设备、变量的数量限制,防止OOM

feat: 优化 ``opcuaserver`` 插件刷新变量逻辑

refactor: taos插件 表名称 强制小写

refactor: 数据库插件 脚本父类更改
2025-03-24 11:23:08 +08:00
Diego
e5ad5a8521 feat: 变量新建时自动设置已选择的设备 2025-03-21 16:46:40 +08:00
Diego
3942819fed refactor: ui样式修改 2025-03-21 10:48:11 +08:00
Diego
1cc5f538aa feat: 报警实体添加 `恢复值` 属性 2025-03-20 11:04:55 +08:00
Diego
b0d8534ea4 fix: 规则引擎按钮权限异常 2025-03-19 17:46:17 +08:00
Diego
5b2bc38670 refactor: 删除不需要的代码 2025-03-19 17:09:42 +08:00
Diego
b36aa3557c feat: opcUaServer支持同时多个写入 2025-03-19 10:57:57 +08:00
Diego
a1a525e6e3 fix: 导出excel失败 2025-03-19 09:42:37 +08:00
Diego
14453cc437 fix: rpc日志入队条件错误 2025-03-19 08:45:24 +08:00
Diego
db760ba5d8 fix: taos插件sql语句缺失标识符 2025-03-18 17:01:02 +08:00
Diego
4ae692008f 资源文件翻译错别字 2025-03-18 15:54:08 +08:00
Diego
f37449f9ac feat: 新增在线excel编辑 2025-03-18 15:44:00 +08:00
Diego
8b7348b3d4 build: 10.1.20
feat: 优化excel导入速度
feat: 优化taos插件插入数据速度
feat: DB插件全部增加设备名称筛选项

refactor: 更改默认sqlite数据库目录

fix: taos插件多个插件设备但同个变量名称的插入逻辑错误
2025-03-17 19:39:19 +08:00
Diego
709cbb7b2e build: 10.1.19 2025-03-16 23:35:52 +08:00
2248356998 qq.com
187f1a4ce2 refactor: 更新依赖项 2025-03-16 23:31:37 +08:00
2248356998 qq.com
0f2ebd3a54 refactor: 调整发送延迟方法位置 2025-03-15 14:16:57 +08:00
Diego
86b63c2792 fix: 动态topic推断错误 2025-03-14 17:02:04 +08:00
Diego
cec2e71b41 build: 10.1.18
feat: opcuaMaster兼容性增强
refactor: controlLogix插件改为符号方式
2025-03-14 13:53:13 +08:00
Diego
9380f3e1ec feat: opcua 兼容性增强 2025-03-13 17:36:45 +08:00
Diego
76c2934e12 fix: modbusSlave驱动插件配置错误 2025-03-13 10:08:01 +08:00
Diego
9714c35d4b refactor: 修改依赖项版本 2025-03-13 09:14:24 +08:00
Diego
0d9beb713a refactor: 修改依赖项版本 2025-03-13 09:10:57 +08:00
Diego
c5f8d0e0b7 feat: 添加DTU服务类型字段 2025-03-12 23:14:23 +08:00
youthalan
5e8f4b075d !59 用户实体 语言json资源更改
* 用户实体 语言json资源更改
2025-03-11 08:23:26 +00:00
Diego
4b53fca3db build: 10.1.13 2025-03-11 14:39:57 +08:00
Diego
5d9c030a09 feat: 添加json缩进格式配置 2025-03-11 14:00:35 +08:00
Diego
bacbb9a28e fix: opcuaserver释放时空异常 2025-03-10 13:32:20 +08:00
Diego
7c287e0c03 refactor: 同步依赖库代码 2025-03-10 11:48:52 +08:00
Diego
10f675137d build: 10.1.9 2025-03-09 23:04:59 +08:00
2248356998 qq.com
6bdb5328e3 feat: mqttclient插件支持tls 2025-03-09 22:53:22 +08:00
2248356998 qq.com
14f529ac4e refactor: 调整授权cookie名称 2025-03-09 19:34:41 +08:00
Diego
ffe96d9ef1 build: 10.1.8 2025-03-09 10:23:21 +08:00
2248356998 qq.com
335f264caa refactor: 日志/缓存文件夹 由Id 改为 名称 2025-03-09 10:22:05 +08:00
2248356998 qq.com
b73e86ea9f refactor: 方法名称错别字 2025-03-07 20:51:36 +08:00
Diego
2bc2c4c57b refactor: table默认紧凑 2025-03-07 16:59:28 +08:00
Diego
6f87c2f03a refactor: 优化UI细节 2025-03-07 16:14:13 +08:00
Diego
75e677af88 feat: web页面更新为紧凑主题 2025-03-07 15:47:39 +08:00
Diego
9ffe355396 build: 10.1.5
feat: 添加复制通道、复制设备功能
refactor: 优化UI提示
2025-03-07 12:21:09 +08:00
Diego
60b0fb23f8 refactor: 调整模块顺序 2025-03-07 08:52:43 +08:00
Diego
1c05ffc6ed build: 10.1.4 2025-03-06 21:17:14 +08:00
Diego
8cd4e43c68 fix: web页面 业务设备节点查询变量空异常 2025-03-06 20:55:45 +08:00
Diego
8486d28310 refactor: sql错误输出文本日志 2025-03-06 18:00:16 +08:00
Diego
e8aadda185 refactor: 显示未知类型插件 2025-03-06 17:46:28 +08:00
Diego
fcfd0428ab fix: s7设备无法保存 2025-03-06 16:33:43 +08:00
Diego
ddcc087800 build: 10.1.2
refactor: EnableWriteXorExecute 默认0
fix: 集合多线程更改错误
2025-03-06 12:09:50 +08:00
Diego
d574de54b4 refactor: EnableWriteXorExecute 默认0 2025-03-06 11:43:50 +08:00
Diego
1682ac03ac build: 10.1.1
fix: 变量寄存器编辑UI 输出错误
2025-03-06 11:08:52 +08:00
Diego
447dbece1c refactor: variableRuntime dispose 2025-03-06 10:45:39 +08:00
Diego
dcca901b17 refactor: 调整生产环境配置 2025-03-06 10:39:36 +08:00
Diego
a32c779074 refactor: 日志显示刷新频率降低 2025-03-06 09:13:30 +08:00
Diego
187c0f9829 refactor: 添加过滤条件 2025-03-06 08:44:55 +08:00
Diego
6c970c88ab feat: 优化组态重载逻辑 2025-03-06 00:46:24 +08:00
Diego
28a4520c77 build: 添加包构建条件 2025-03-05 16:37:21 +08:00
Diego
02d0dcdff7 fix: 变量页面筛选总条数显示错误 2025-03-05 14:58:17 +08:00
Diego
427d4e31c9 build: 10.1.0.4
fix: 包构建缺失内容
2025-03-05 14:40:09 +08:00
Diego
c60f7aac44 build: 10.1.0.3 2025-03-05 11:50:10 +08:00
Diego
42a7758a34 fix: webapi rpc写入 json反序列化错误 2025-03-05 11:26:38 +08:00
Diego
e0bade4670 fix: 规则引擎 变量Node 查询名称空异常 2025-03-04 23:12:53 +08:00
Diego
6af83152de build: 10.1.0.1
feat: 变量名称 由 全局唯一 更改为 采集设备内唯一
fix: opcda订阅方式初始化时未更新变量状态,导致后续变了状态一直是offline
feat: mqttServer 允许的连接ID前缀 为空时可为任意id

破坏性更新:
1、兼容旧版本数据库,但不兼容旧版本excel文件,不同点在于变量excel 业务属性页,添加了设备名称和业务设备名称,旧版本只有业务设备名称
2、业务上传内容更改为 字典类型,键为设备名称,值为变量列表。变量列表内容与旧版本相同
2025-03-04 21:09:11 +08:00
Diego
091a30045d feat: 规则引擎触发器UI优化 2025-03-03 18:07:18 +08:00
Diego
b2c8ae0c33 更新依赖包 2025-03-03 13:02:27 +08:00
Diego
8b6e0982c6 feat: 页面添加变量寄存器格式提示titile 2025-03-01 21:00:24 +08:00
Diego
caa3ebd600 feat: db动态脚本基类修改 2025-03-01 16:04:40 +08:00
Diego
d248e047b3 build: 10.0.2.14 2025-02-27 18:04:51 +08:00
Diego
25a95a1de0 fea(variableRuntimeService): 方法添加是否重启参数 2025-02-27 17:48:03 +08:00
Diego
d506b2c692 fix: api登录异常 2025-02-27 11:11:53 +08:00
Diego
24c672c2b9 feat: 优化树节点右键菜单内容 2025-02-27 10:51:05 +08:00
Diego
c407e0e83e feat: taos时序库支持每个数据点独立子表
feat: 重启运行时方法改为重载数据库获取通道设备变量
2025-02-26 21:59:41 +08:00
Diego
b1346698d6 feat: 优化opcua历史查询 2025-02-26 14:40:38 +08:00
Diego
5c01129567 补充解决方案文件 2025-02-25 16:39:03 +08:00
Diego
cd7ce53bd0 feat: 新增webhook插件 2025-02-25 16:23:43 +08:00
Diego
4c95c20061 feat: 添加动态变量新增和删除的方法 2025-02-25 15:21:06 +08:00
Diego
5339db5058 refactor: 删除opcua105 2025-02-25 10:04:02 +08:00
Diego
623e932f8c feat: 增加 microLogix1400 plc驱动(pro插件) 2025-02-24 20:23:07 +08:00
Diego
43659de4f4 feat: mqttServer增加匿名登录选项 2025-02-23 22:22:47 +08:00
Argo Zhang
2a661eda42 refactor: 调整插件调试样式 (#23) 2025-02-23 22:03:51 +08:00
Diego
c85a93a17b build: 10.0.2.3 2025-02-21 08:45:04 +08:00
2248356998 qq.com
8ab36cb3e1 feat: 优化opcua订阅 2025-02-20 23:18:37 +08:00
Diego
72820d0708 feat: 添加注册服务 2025-02-20 11:33:39 +08:00
Diego
95b551d4ba feat: 优化opc单设备大批变量检索速度
fix: 定时任务删除网关日志无效
2025-02-19 20:39:42 +08:00
Diego
68af9a0695 feat: 语言资源 2025-02-18 19:33:14 +08:00
Diego
6953c3989b feat: 添加刷新变量日志 2025-02-18 09:03:51 +08:00
Diego
cd4cb385d5 fix: 修复设备冗余多次切换后设备状态异常导致切换事件不触发 2025-02-17 14:58:44 +08:00
Diego
f4f6d595fc feat: 优化设备冗余,网关冗余 2025-02-17 13:31:29 +08:00
Diego
d2356f00e9 feat: 业务插件增加循环间隔 2025-02-17 09:35:59 +08:00
2248356998 qq.com
e408136196 feat: 优化组态配置流畅度 2025-02-15 20:12:59 +08:00
Diego
c355968add 支持冗余备用设备变量并集 2025-02-14 17:15:58 +08:00
Diego
783f878947 fix: 转换器实例初始化方法 2025-02-14 16:23:00 +08:00
Diego
4faf41e482 fix: 修复设备冗余切换 2025-02-14 14:48:28 +08:00
Diego
d8964b6ed2 feat: 优化设备冗余 2025-02-14 11:11:12 +08:00
Diego
b5b999941c 10.0.1.3 2025-02-14 09:43:15 +08:00
2248356998 qq.com
deeba8c4de 默认关闭多租户 2025-02-13 20:33:19 +08:00
youthalan
9e48729eb0 !58 修复按钮资源值及默认开启租户状态
* 修复按钮资源值及默认开启租户状态
2025-02-13 12:32:26 +00:00
2248356998 qq.com
936e374c05 fix: 修改设备后,变量实体中的设备名称实时生效 2025-02-13 20:29:50 +08:00
Diego
0f3524b114 feat: opc插件支持实时修改变量 2025-02-12 16:23:05 +08:00
Diego
d62de3d72c feat: 上传 网关冗余/更新以及规则引擎 源码 2025-02-10 09:32:02 +08:00
Diego
957a80da4b feat: 优化变量寄存器界面加载 2025-02-07 18:09:51 +08:00
Diego
1dccd70f7c feat(uaserver): 删除登录策略限制 2025-02-07 16:37:25 +08:00
Diego
006a5d4e61 feat: excel导入支持空白单元格 2025-02-07 10:50:33 +08:00
Diego
ce7cdcf54e fix: 变量编辑样式修正 2025-02-07 10:17:12 +08:00
Diego
c1ebb4a79b feat: 公开mqttServer发布方法 2025-02-06 16:43:05 +08:00
Diego
41964ae1de fix: 修改通道时,设备没有及时修改插件 2025-02-06 16:30:51 +08:00
Diego
66cf34adf7 fix: 修改通道时,设备没有及时修改插件 2025-02-06 16:04:49 +08:00
Diego
9e9e0009cf fix: opcua插件名称重复 2025-02-06 14:26:51 +08:00
Diego
d5cd9660fc feat: 调整blazor授权,添加自定义刷新逻辑 2025-02-06 12:08:40 +08:00
Diego
12a7803653 更新readme 2025-02-05 11:47:42 +08:00
Diego
8f7bef6a20 feat: 添加1.05版本opcua依赖库 2025-02-05 10:49:22 +08:00
2248356998 qq.com
a0715b830c feat: 日志读取长度限制放宽 2025-02-03 21:22:37 +08:00
2248356998 qq.com
f87ce10dc4 feat: 更新依赖包 2025-02-03 19:57:16 +08:00
2248356998 qq.com
ee4936b8c9 fix: 变量实时值偶发错误
feat: 优化内存占用
2025-01-25 16:02:06 +08:00
2248356998 qq.com
3cb2592660 fix: 清空变量时空异常 2025-01-24 23:22:41 +08:00
2248356998 qq.com
574c901b40 v10迁移 2025-01-24 22:42:26 +08:00
2248356998 qq.com
7590ba4ad5 更新插件 2025-01-06 20:57:36 +08:00
Diego
39ce18f8bb 更新readme 2025-01-06 16:06:01 +08:00
Diego
777a4f9d3f 更新插件 2025-01-06 14:29:51 +08:00
Diego
b91a938787 更新构建脚本 2025-01-06 14:02:41 +08:00
Diego
c903abdb1b 更新admin 2025-01-06 11:24:49 +08:00
Diego
3279bc0580 更新构建脚本 2025-01-06 11:12:04 +08:00
Diego
bd32c9f49e 更新插件 2025-01-06 10:28:14 +08:00
Diego
5507088e3d 更新插件 2025-01-06 09:48:55 +08:00
2248356998 qq.com
37ad48ae4a 7.2.3.6
更新依赖包
2025-01-05 23:46:31 +08:00
2248356998 qq.com
120c6e6d87 7.2.3.5
opcuaclient: 心跳机制异常
2025-01-05 00:46:52 +08:00
2248356998 qq.com
866e907b7e 同步 2025-01-02 20:47:47 +08:00
2248356998 qq.com
2bd8c2cb10 更新mc1e插件 2025-01-02 20:45:19 +08:00
Diego
c6c9919178 更新依赖包 2025-01-02 16:45:31 +08:00
Diego
575bf9d1e0 update Version MSBuildProperty 2025-01-02 13:56:38 +08:00
Diego
82a56e0285 更新构建脚本 2025-01-02 12:56:52 +08:00
Diego
25d5f7c132 设定动态编译缓存目录 2025-01-02 12:27:57 +08:00
Diego2098
a3e9ecf30f !56 恢复插件隔离
* 恢复插件域
2025-01-02 04:03:14 +00:00
2248356998 qq.com
ec5ff0a07f 7.2.1.3
更新mqttnet版本
更新admin
2025-01-01 16:25:19 +08:00
Diego
be836d30c5 更新admin 2024-12-30 12:11:08 +08:00
2248356998 qq.com
42b1529a5f 添加Release解决方案 2024-12-28 10:35:02 +08:00
2248356998 qq.com
47708c4807 添加Release解决方案 2024-12-28 10:30:17 +08:00
2248356998 qq.com
d2a51e004c 7.2.0.99
规则引擎:自定义脚本接口定义修改,增加Logger属性
数据库上传插件:回退修改,恢复string类型Value字段
2024-12-28 04:19:37 +08:00
2248356998 qq.com
ab14230101 修复通道报文日志失效 2024-12-26 21:36:09 +08:00
Diego
4bcf8c1f78 修复设备通讯变量点位 2024-12-26 15:38:19 +08:00
Diego
44d00e9da3 数据库插件实体类修改! 2024-12-26 14:45:25 +08:00
Diego
d7e6a4493c 更新依赖 2024-12-26 13:22:51 +08:00
Diego
53e89d8c54 7.2.0.95
修复 LL优先级比L低
添加报警属性验证规则
2024-12-25 12:26:06 +08:00
Diego
4497c13634 代码清理 2024-12-25 10:53:43 +08:00
Diego
4a35fade46 更新插件 2024-12-24 17:44:51 +08:00
Diego
6cba0601fd fix: 上一提交导致other通道创建错误 2024-12-24 17:21:10 +08:00
Diego
ed4332ea78 更新readme 2024-12-24 14:14:30 +08:00
Diego
aba069cec5 7.2.0.91
修复dlt645驱动
调整异步方法
2024-12-24 14:03:39 +08:00
Diego
8a82ac0a11 更新测试方法 2024-12-23 16:43:35 +08:00
Diego
1cd1456d75 7.2.0.87
更新admin
2024-12-23 15:31:16 +08:00
Diego
b791a3eb10 修复 动态插件属性 验证错误 2024-12-23 15:21:42 +08:00
2248356998 qq.com
3b22a8b170 7.2.0.85
更新依赖
2024-12-22 15:59:15 +08:00
2248356998 qq.com
419e8214ca 更新规则引擎,优化性能 2024-12-20 22:32:37 +08:00
Diego
b9f8571f0f 7.2.0.83
fix(VariableRuntime): 修改判断变量变化方法
feat(TestVariable): 修改添加测试变量UI
2024-12-20 14:28:44 +08:00
Diego
c6d9a9d7f8 修改判断变量变化方法 2024-12-20 14:27:29 +08:00
Diego
310aba6ccb 修改添加测试变量UI 2024-12-20 12:11:20 +08:00
Diego
9bd89ac4f6 添加 git 更新子模块 bat 2024-12-19 18:40:13 +08:00
Diego
ea6a51dca9 更新admin 2024-12-19 18:16:01 +08:00
Diego
e701bcc50c 调整插件基类,增加上传模式配置 2024-12-19 18:15:47 +08:00
Diego
a1abf06e75 7.2.0.78
更新opc插件
2024-12-19 16:11:13 +08:00
Diego
76ace394b0 更新opcua插件 2024-12-19 15:21:21 +08:00
Diego
4dac462f8f 更新解决方案 2024-12-19 12:21:32 +08:00
Diego
039672b1e7 更新admin 2024-12-19 12:19:55 +08:00
Diego
1b26ecbbf5 添加 admin 子模块 2024-12-19 11:54:14 +08:00
Diego2098
f4a7e96943 add Admin submodule.
Signed-off-by: Diego2098 <2248356998@qq.com>
2024-12-19 03:17:59 +00:00
Diego
a45bc2954f 移动文件夹 2024-12-19 11:16:24 +08:00
Diego
62f32467b7 移动文件夹 2024-12-19 11:15:18 +08:00
Diego
600a1bf201 7.2.0.75 2024-12-18 12:36:49 +08:00
Diego
7e196e7aa6 更新admin 2024-12-17 14:16:15 +08:00
Diego
9ad3507b66 7.2.0.72
修复变量导入错误
2024-12-17 13:51:51 +08:00
Diego
add1bdfcf6 更新admin 2024-12-17 13:26:27 +08:00
Diego
7ea8a7c079 7.2.0.70
修复初始值解析
2024-12-17 10:51:52 +08:00
Diego
28e31f5165 7.2.0.69
更新实体特性
2024-12-17 10:22:03 +08:00
Diego
10a6975c5d 7.2.0.68
更新modbusSlave插件,适配03功能码单写bit
2024-12-16 18:24:16 +08:00
Diego
b34ea87660 7.2.0.67
更新db插件,增加 单独配置实时表上传间隔时间 属性
2024-12-16 17:38:42 +08:00
Diego
86ed69c50b 7.2.0.66
更新版本
2024-12-16 11:29:52 +08:00
Diego
02e824154c 7.2.0.65
读取间隔增加cron支持
2024-12-16 11:13:23 +08:00
Matrix
dc973c8491 !55 TimeTick 新增 Cron
* TimeTick 新增 cron定时
2024-12-16 03:03:39 +00:00
2248356998 qq.com
0a28e3a8d3 7.2.0.64 2024-12-15 23:41:07 +08:00
2248356998 qq.com
7a48c260e1 更新插件 2024-12-14 15:22:19 +08:00
Diego
c55c49a3a2 添加TS自定义二进制序列化方式 2024-12-13 12:19:12 +08:00
Diego
0fc9b06d12 7.2.0.62
优化上传插件批量性能
2024-12-11 15:52:00 +08:00
Diego
d71ad5a6bf 单文件发布 2024-12-11 11:13:58 +08:00
Diego
f0b3028306 更新规则引擎 2024-12-11 09:15:27 +08:00
2248356998 qq.com
752992c527 7.2.0.60
更新admin
删除不需要的文件
2024-12-10 14:30:18 +08:00
Diego
43c4476396 7.2.0.58
更新aop框架版本
2024-12-09 15:43:29 +08:00
Diego
f8172bed56 修复单文件发布 2024-12-09 11:33:00 +08:00
Diego
c1f71b4cfc 7.2.0.57
添加数据库解密示例

更新依赖包
2024-12-09 11:11:16 +08:00
2248356998 qq.com
4799da15e7 7.2.0.53
更新语言资源
更新规则引擎
2024-12-08 22:01:16 +08:00
2248356998 qq.com
4936e47c7a 7.2.0.50 2024-12-07 18:34:51 +08:00
2248356998 qq.com
cb965373aa 更新插件 2024-12-07 18:30:25 +08:00
2248356998 qq.com
654a91184a 7.2.0.48
更新依赖
2024-12-07 17:51:48 +08:00
Diego
57997f7f4b 优化规则引擎 2024-12-06 10:45:56 +08:00
Diego
ca2f5be3d1 7.2.0.46
更新Admin,修复错误插件dll导致程序无法启动的问题
2024-12-05 17:15:54 +08:00
Diego
1b091073f1 7.2.0.45 2024-12-05 11:10:43 +08:00
Diego
85d2d3c442 更新pro插件 2024-12-05 11:09:31 +08:00
Diego
d77dfd63cf 增加规则引擎插件 2024-12-05 02:13:02 +08:00
Diego
db2bf52fca 调整脚本编辑UI 2024-12-04 08:48:27 +08:00
Diego
2c62d97440 迁移重启后台按钮到设备状态页面 2024-12-03 22:23:26 +08:00
Diego
53b2e64214 修复net6 界面白屏 2024-12-02 18:11:16 +08:00
Diego
ccf82a3ee5 添加layout显示条件 2024-12-02 17:08:50 +08:00
Diego
9cb6f35e60 修改代码规范文件,更新依赖包 2024-12-02 11:28:54 +08:00
2248356998 qq.com
da2e33a040 7.2.0.36 2024-11-30 21:00:05 +08:00
2248356998 qq.com
f2d260bfa4 动态调整线程池最大活动数目 2024-11-30 01:53:26 +08:00
Diego
783c7244f9 修正語言資源 2024-11-29 17:05:21 +08:00
Diego
c9287e0e63 更新依赖
命名空间变化
2024-11-29 16:53:00 +08:00
Diego
69b84d115c 添加变量导入校验缓存 2024-11-29 10:01:02 +08:00
Diego
1745b898b1 删除文件 2024-11-29 09:22:36 +08:00
Diego
5ba4c85249 更新依赖包,转移部分代码到插件仓库 2024-11-28 21:14:14 +08:00
Diego
03c87bb46d 更新admin 2024-11-28 14:13:50 +08:00
Diego
dff5a76e9e 更新docker构建脚本 2024-11-27 16:05:16 +08:00
Diego
50386f4ca4 cache try dispose 2024-11-26 11:29:52 +08:00
Diego
de6ba2ec80 更新版本 2024-11-25 13:26:11 +08:00
Diego
ce126015d9 更新pro插件 2024-11-25 12:52:04 +08:00
Diego
f2337c3d43 更新依赖 2024-11-25 11:38:09 +08:00
Diego
41b59d322a 更新依赖,添加bacnet,HJ212 2024-11-25 11:32:28 +08:00
2248356998 qq.com
dfea7c111e 更新依赖 2024-11-23 17:36:59 +08:00
2248356998 qq.com
3d5d672297 更新版本 2024-11-22 19:57:31 +08:00
2248356998 qq.com
e4dcc6f342 更新插件 2024-11-22 19:57:11 +08:00
Diego
9847249611 修改非数采插件的引用方式 2024-11-21 23:52:31 +08:00
Diego
19742a54ec 更新插件 2024-11-21 18:37:11 +08:00
Diego
5af9ae7a80 修复上个版本导致驱动sign量无法正常返回的bug 2024-11-21 17:42:11 +08:00
Diego
78962a4a75 去掉api日志显示 2024-11-21 17:05:13 +08:00
Diego
9a35d826aa 添加 更新服务 启用选项 2024-11-21 16:47:09 +08:00
Diego
c9cdc89d35 更新版本 2024-11-20 18:28:16 +08:00
Diego
33b75703ba 更新種子數據 2024-11-20 09:40:49 +08:00
Diego
ca55aa3a24 更新插件 2024-11-19 17:03:52 +08:00
Diego
95307c3902 更新版本文件 2024-11-19 12:10:40 +08:00
Diego
89c8eae7e2 更新依赖 2024-11-19 10:48:49 +08:00
Diego
91614772dc 更新版本 2024-11-19 10:28:42 +08:00
Diego
cd497ab4dc 更换快捷方式实现方式 2024-11-18 10:03:45 +08:00
2248356998 qq.com
f81895905e 更新依赖包 2024-11-18 00:24:18 +08:00
2248356998 qq.com
d82fafaa7a 更新后台版本 2024-11-17 14:14:28 +08:00
2248356998 qq.com
0ea01acc46 驱动在丢弃错误报文时提示日志 2024-11-16 20:58:46 +08:00
2248356998 qq.com
df55792a68 调试工具css更新 2024-11-16 20:29:36 +08:00
Diego
6a19c45269 更新版本 2024-11-15 21:51:39 +08:00
Diego
23ee345441 更新插件 2024-11-15 21:13:43 +08:00
Diego
0e8ff253f3 更新admin版本 2024-11-15 16:53:44 +08:00
2248356998 qq.com
89bddb0bc4 修复双机热备功能,添加日志显示 2024-11-14 22:34:03 +08:00
Diego
77bbca9bb3 调整公共页面位置 2024-11-14 15:13:34 +08:00
2248356998 qq.com
1acd4d8d58 更新版本 2024-11-14 00:31:33 +08:00
2248356998 qq.com
4db72a712d 更新依赖 2024-11-13 22:08:59 +08:00
Diego
da2b7ba08e 调整css 2024-11-13 17:32:39 +08:00
Diego
f8b49411bf 添加net9.0 2024-11-13 16:15:04 +08:00
Diego
a04b0d99b0 添加更新服务器 2024-11-12 16:14:09 +08:00
Diego
9e9735f617 更新admin版本 2024-11-11 17:28:34 +08:00
Diego
f906b36f01 更新插件版本 2024-11-11 17:26:12 +08:00
Diego
a9d1f4f854 更新版本 2024-11-10 20:29:42 +08:00
Diego
d017ae905f 添加重启软件功能 2024-11-10 01:18:05 +08:00
Diego
bede280507 种子数据默认不更新 2024-11-08 00:35:08 +08:00
Diego
ea130047bc 修复配置 2024-11-07 16:36:18 +08:00
Diego
d77d8de63e 更新依赖包 2024-11-07 16:27:44 +08:00
Diego
f5413d9202 修复插件关联设备数量缓存不刷新 2024-11-07 15:08:15 +08:00
Diego
559fc8f216 更新后台 2024-11-06 16:25:10 +08:00
Diego
34b2df911d 更新插件版本 2024-11-06 13:16:47 +08:00
Diego
3d5a8a8cbd Merge branch 'master' into release 2024-11-06 10:19:16 +08:00
Diego
2b07097e14 导入变量时写入机构id 2024-11-06 10:18:52 +08:00
Diego
372e943d8b Merge branch 'master' into release 2024-11-06 09:32:24 +08:00
Diego
95afcedc4f 更新后台 2024-11-06 09:32:01 +08:00
Diego
c1dc8f017c Merge branch 'master' into release 2024-11-06 09:15:04 +08:00
Diego
dbeddca263 整理格式 2024-11-06 09:13:09 +08:00
Diego
e65d133dec 更新依赖包 2024-11-06 09:02:11 +08:00
Diego
b5e1dea1c2 Merge branch 'master' into release 2024-11-05 14:52:58 +08:00
Diego
3f19469939 更新后台 2024-11-05 14:52:27 +08:00
Diego
87874b037a Merge branch 'master' into release 2024-11-05 11:59:50 +08:00
Diego
adda6841d1 更新后台版本 2024-11-05 11:58:24 +08:00
Diego
a8092b921b 添加机构 2024-11-05 06:05:23 +08:00
Diego
930e4d69d1 恢复sqlite数据库类型 2024-11-05 05:41:07 +08:00
Diego
d9219df45b 后台管理添加机构功能 2024-11-05 05:39:07 +08:00
Diego
a6d4ec28db 删除不必要的构建 2024-10-29 14:26:06 +08:00
Diego
b29e13f777 更新构建脚本 2024-10-28 17:08:35 +08:00
Diego
9363b78ff6 Merge branch 'master' of https://gitee.com/ThingsGateway/ThingsGateway 2024-10-28 16:58:42 +08:00
Diego
eba411fec7 更新插件 2024-10-28 16:49:09 +08:00
Diego2098
9afe99e134 !53 7.0.1.6
* Merge branch 'release' of gitee.com:ThingsGateway/ThingsGateway into master
* 更新依赖包
* 更新依赖包,雪花ID方法
* 更新依赖包,雪花ID方法
2024-10-28 00:40:55 +00:00
Diego2098
6678856178 Merge branch 'release' of gitee.com:ThingsGateway/ThingsGateway into master
Signed-off-by: Diego2098 <2248356998@qq.com>
2024-10-28 00:40:40 +00:00
Diego
a1fea38c2c 更新依赖包 2024-10-28 08:32:18 +08:00
Diego2098
2d3265e66d !52 7.0.1.5
* 更新依赖包,雪花ID方法
* 更新依赖包,雪花ID方法
2024-10-23 11:03:51 +00:00
Diego
f6d4400db9 更新依赖包,雪花ID方法 2024-10-23 19:01:13 +08:00
Diego
6d892a9266 更新依赖包,雪花ID方法 2024-10-23 19:00:23 +08:00
Diego2098
356fa08da5 !51 7.0.1.4
Merge pull request !51 from Diego2098/master
2024-10-16 15:13:04 +00:00
Diego
2225f8c890 修复数组类型比较错误 2024-10-16 22:37:35 +08:00
Diego2098
d1a89739f9 !50 7.0.1.3
Merge pull request !50 from Diego2098/master
2024-10-16 05:23:38 +00:00
Diego
6ec6896763 Merge branch 'master' of https://gitee.com/dotnetchina/ThingsGateway 2024-10-16 13:19:42 +08:00
Diego
9fb718d610 更新依赖包 2024-10-16 13:18:09 +08:00
Diego2098
c913b89a47 !49 7.0.1.2
Merge pull request !49 from Diego2098/master
2024-10-16 03:59:53 +00:00
Diego2098
58b45693b7 !48 7.0.1.2
Merge pull request !48 from Diego2098/release
2024-10-16 03:59:16 +00:00
Diego
29d3ece9b1 更新依赖包 2024-10-15 17:47:12 +08:00
Diego2098
7e5db75230 !47 7.0.1.2
Merge pull request !47 from Diego2098/master
2024-10-14 11:51:48 +00:00
Diego
283c0b9845 修改:更新脚本编辑UI 2024-10-14 19:51:22 +08:00
Diego2098
071a0f1651 !46 7.0.1.1
Merge pull request !46 from Diego2098/master
2024-10-14 11:07:55 +00:00
Diego
1a116557a4 修改:移入部分项目代码 2024-10-14 19:06:09 +08:00
Diego
1468297626 修改:更新依赖包 2024-10-12 23:41:40 +08:00
Diego
87e94fdf52 修改:更新依赖包 2024-10-12 10:59:12 +08:00
Diego
64e15c33c8 修改:更新依赖包 2024-10-12 10:57:10 +08:00
Diego2098
7170f435d1 !45 7.0.0.26
Merge pull request !45 from Diego2098/master
2024-10-11 07:31:21 +00:00
Diego
9942be0493 修复:更新依赖包,修复脚本编辑框无法显示的问题 2024-10-11 15:29:45 +08:00
Diego
eaad36277f 修改:脚本编辑更改代码编辑器 2024-10-10 17:25:37 +08:00
Diego
37cc498568 修改:脚本编辑更改代码编辑器 2024-10-10 16:44:39 +08:00
3331 changed files with 427691 additions and 21909 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

7
.gitignore vendored
View File

@@ -360,4 +360,9 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
/src/*Pro*/
/src/*Pro*
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
/src/.idea/

View File

@@ -85,7 +85,7 @@
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
as of the date such litigation is field.
4. Cachetribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without

View File

@@ -5,7 +5,7 @@
## Introduction

A cross-platform, high-performance edge data collection gateway based on net8, capable of handling millions of data points per.
A cross-platform, high-performance edge data collection gateway based on net9.

## Documentation
@@ -16,6 +16,32 @@ A cross-platform, high-performance edge data collection gateway based on net8, c
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)

## Demo

[Demo](https://demo.thingsgateway.cn/)

Account: **SuperAdmin**

Password: **111111**

**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
```
### Plugin List

@@ -30,6 +56,7 @@ A cross-platform, high-performance edge data collection gateway based on net8, c
| Dlt6452007 | Supports Serial/Tcp/Udp links |
| OpcDaMaster | Compiled for 64-bit |
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
| Webhook | Webhook |
#### Business Plugins
@@ -52,29 +79,8 @@ A cross-platform, high-performance edge data collection gateway based on net8, c
## License

[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)
[License](https://thingsgateway.cn/docs/1)

## Demo

[Demo](http://47.119.161.158:5000/)

Account: **SuperAdmin**

Password: **111111**

**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
```

## Sponsorship

View File

@@ -2,7 +2,7 @@
## 介绍
基于net8的跨平台高性能边缘采集网关,单机采集数据点位可达百万
基于net9的跨平台高性能边缘采集网关
## 文档
@@ -10,6 +10,27 @@
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
## 演示
[ThingsGateway演示地址](https://demo.thingsgateway.cn/)
账户 : **SuperAdmin**
密码 : **111111**
**右上角个人弹出框中,切换到物联网关模块**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
```
### 插件列表
#### 采集插件
@@ -38,29 +59,12 @@
| SqlHistoryAlarm | 报警历史数据关系数据库存储 |
| TDengineDB | 时序数据库存储 |
| QuestDB | 时序数据库存储 |
| Webhook | Webhook |
## 协议
[Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE)
[版权声明](https://thingsgateway.cn/docs/1)
## 演示
[ThingsGateway演示地址](http://47.119.161.158:5000/)
账户 : **SuperAdmin**
密码 : **111111**
**右上角个人弹出框中,切换到物联网关模块**
## Docker
```shell
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
```
## 赞助
@@ -73,3 +77,10 @@ QQ群605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcN
## Pro插件
[插件列表](https://thingsgateway.cn/docs/1001)
## 特别声明
ThingsGateway 项目已加入 [dotNET China](https://gitee.com/dotnetchina) 组织。<br/>
![dotnetchina](https://gitee.com/dotnetchina/home/raw/master/assets/dotnetchina-raw.png "dotNET China LOGO")

View File

@@ -1,248 +1,133 @@
root = true
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
###############################
# Core EditorConfig Options #
###############################
###############################
# Core EditorConfig Options #
###############################
# All files
[*]
indent_style = space
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
spelling_exclusion_path = .\exclusion.dic
# Microsoft .NET properties
csharp_new_line_before_members_in_object_initializers = false
csharp_preferred_modifier_order = public, private, protected, internal, file, new, static, abstract, virtual, sealed, readonly, override, extern, unsafe, volatile, async, required:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
dotnet_naming_rule.unity_serialized_field_rule.import_to_resharper = True
dotnet_naming_rule.unity_serialized_field_rule.resharper_description = Unity serialized field
dotnet_naming_rule.unity_serialized_field_rule.resharper_guid = 5f0fdb63-c892-4d2c-9324-15c80b22a7ef
dotnet_naming_rule.unity_serialized_field_rule.severity = warning
dotnet_naming_rule.unity_serialized_field_rule.style = lower_camel_case_style
dotnet_naming_rule.unity_serialized_field_rule.symbols = unity_serialized_field_symbols
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_accessibilities = *
dotnet_naming_symbols.unity_serialized_field_symbols.applicable_kinds =
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_applicable_kinds = unity_serialised_field
dotnet_naming_symbols.unity_serialized_field_symbols.resharper_required_modifiers = instance
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:none
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
dotnet_style_qualification_for_event = false:suggestion
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
# ReSharper properties
resharper_autodetect_indent_settings = true
resharper_formatter_off_tag = @formatter:off
resharper_formatter_on_tag = @formatter:on
resharper_formatter_tags_enabled = true
resharper_new_line_before_while = true
resharper_place_attribute_on_same_line = false
resharper_show_autodetect_configure_formatting_tip = false
resharper_use_indent_from_vs = false
# ReSharper inspection severities
resharper_arrange_redundant_parentheses_highlighting = hint
resharper_arrange_this_qualifier_highlighting = hint
resharper_arrange_type_member_modifiers_highlighting = hint
resharper_arrange_type_modifiers_highlighting = hint
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
resharper_built_in_type_reference_style_highlighting = hint
resharper_redundant_base_qualifier_highlighting = warning
resharper_suggest_var_or_type_built_in_types_highlighting = hint
resharper_suggest_var_or_type_elsewhere_highlighting = hint
resharper_suggest_var_or_type_simple_types_highlighting = hint
resharper_web_config_module_not_resolved_highlighting = warning
resharper_web_config_type_not_resolved_highlighting = warning
resharper_web_config_wrong_module_highlighting = warning
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
[*.{cs,css,js,json,*html,razor,txt,log}]
charset = utf-8-bom
[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
[*.json]
indent_size = 2
[*.{ps1,psm1}]
indent_size = 4
[*.sh]
indent_size = 4
end_of_line = lf
###############################
# .NET Coding Conventions #
###############################
[*.{cs,vb}]
# Organize usings
dotnet_sort_system_directives_first = false
# this. preferences
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
###############################
# Naming Conventions #
###############################
# Style Definitions
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# Use PascalCase for constant fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
end_of_line = crlf
dotnet_style_prefer_collection_expression = when_types_exactly_match:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
dotnet_code_quality_unused_parameters = all:suggestion
###############################
# C# Coding Conventions #
###############################
[*.cs]
# var preferences
csharp_style_var_for_built_in_types = true:silent
csharp_style_var_when_type_is_apparent = true:silent
csharp_style_var_elsewhere = true:silent
csharp_prefer_static_local_function = true:silent
# Expression-bodied members
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# 命名样式
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
csharp_using_directive_placement = outside_namespace:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
# Null-checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
# Expression-level preferences
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_prefer_braces = true:silent
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
###############################
# C# Formatting Rules #
###############################
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
# Space preferences
csharp_space_after_cast = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_around_binary_operators = before_and_after
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
# Wrapping preferences
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
###############################
# VB Coding Conventions #
###############################
csharp_style_namespace_declarations = block_scoped:silent
[*.vb]
# Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
[*.cs]
# Add file header
#### 命名样式 ####
# 命名规则
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
# 符号规范
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.类型.required_modifiers =
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
dotnet_naming_symbols.非字段成员.required_modifiers =
# 命名样式
dotnet_naming_style.以_i_开始.required_prefix = I
dotnet_naming_style.以_i_开始.required_suffix =
dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
dotnet_naming_style.帕斯卡拼写法.required_prefix =
dotnet_naming_style.帕斯卡拼写法.required_suffix =
dotnet_naming_style.帕斯卡拼写法.word_separator =
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
file_header_template = ------------------------------------------------------------------------------\n此代码版权声明为全文件覆盖如有原作者特别声明会在下方手动补充\n此代码版权除特别声明外的代码归作者本人Diego所有\n源代码使用协议遵循本仓库的开源协议及附加协议\nGitee源代码仓库https://gitee.com/diego2098/ThingsGateway\nGithub源代码仓库https://github.com/kimdiego2098/ThingsGateway\n使用文档https://thingsgateway.cn/\nQQ群605534569\n------------------------------------------------------------------------------
csharp_style_namespace_declarations = file_scoped:suggestion
csharp_style_expression_bodied_local_functions = true:silent
csharp_using_directive_placement = outside_namespace:silent
csharp_prefer_simple_using_statement = true:suggestion
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_prefer_top_level_statements = true:silent
csharp_style_prefer_primary_constructors = true:suggestion
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_prefer_readonly_struct_member = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
[*.{cs,vb}]
end_of_line = crlf
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_property = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_event = false:silent
dotnet_diagnostic.RCS1146.severity = warning
dotnet_diagnostic.RCS1059.severity = none
dotnet_diagnostic.RCS1138.severity = suggestion
dotnet_code_quality.CA1822.api_surface = private, internal

View File

@@ -0,0 +1,6 @@
## Release 1.0
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|--------------------
TG0001 | Conflict | Error | SetParametersAsyncGenerator

View File

@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\PackNuget.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;</TargetFrameworks>
<Version>$(SourceGeneratorVersion)</Version>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<NoPackageAnalysis>true</NoPackageAnalysis>
<SignAssembly>false</SignAssembly>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<IncludeBuildOutput>false</IncludeBuildOutput>
<!-- 避免 DLL 被打包到 lib/ -->
<EnableSourceGenerator>true</EnableSourceGenerator>
<!-- 可选 -->
</PropertyGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(TargetFileName)" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="AnalyzerReleases.Shipped.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" Private="false" />
</ItemGroup>
</Project>

View File

@@ -7,4 +7,3 @@
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------

View File

@@ -0,0 +1,440 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace Microsoft.AspNetCore.Components;
[Generator]
public sealed partial class SetParametersAsyncGenerator : IIncrementalGenerator
{
private const string m_DoNotGenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
internal sealed class DoNotGenerateSetParametersAsyncAttribute : Attribute { }
}
""";
private const string m_GenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
internal sealed class GenerateSetParametersAsyncAttribute : Attribute
{
public bool RequireExactMatch { get; set; }
}
}
""";
private const string m_GlobalGenerateSetParametersAsyncAttribute = """
using System;
namespace Microsoft.AspNetCore.Components
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
internal sealed class GlobalGenerateSetParametersAsyncAttribute : Attribute
{
public bool Enable { get; }
public GlobalGenerateSetParametersAsyncAttribute(bool enable = true) { Enable = enable; }
}
}
""";
private static readonly DiagnosticDescriptor ParameterNameConflict = new DiagnosticDescriptor(
id: "TG0001",
title: "Parameter name conflict",
messageFormat: "Parameter names are case insensitive. {0} conflicts with {1}.",
category: "Conflict",
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: "Parameter names must be case insensitive to be usable in routes. Rename the parameter to not be in conflict with other parameters.");
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 注入 attribute 源码
context.RegisterPostInitializationOutput(ctx =>
{
ctx.AddSource("DoNotGenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_DoNotGenerateSetParametersAsyncAttribute, Encoding.UTF8));
ctx.AddSource("GenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_GenerateSetParametersAsyncAttribute, Encoding.UTF8));
ctx.AddSource("GlobalGenerateSetParametersAsyncAttribute.g.cs", SourceText.From(m_GlobalGenerateSetParametersAsyncAttribute, Encoding.UTF8));
});
// 筛选 ClassDeclarationSyntax
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
.Where(static c => c is not null);
// 合并 Compilation
var compilationProvider = context.CompilationProvider;
var candidateClasses = classDeclarations.Combine(compilationProvider);
context.RegisterSourceOutput(candidateClasses, static (spc, tuple) =>
{
var (classDeclaration, compilation) = tuple;
Execute(spc, compilation, classDeclaration);
});
}
private static void Execute(SourceProductionContext context, Compilation compilation, ClassDeclarationSyntax classDeclaration)
{
var model = compilation.GetSemanticModel(classDeclaration.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDeclaration);
if (classSymbol is null || classSymbol.Name == "_Imports")
return;
var positiveAttr = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.GenerateSetParametersAsyncAttribute");
var negativeAttr = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.DoNotGenerateSetParametersAsyncAttribute");
if (classSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, negativeAttr)))
return;
if (!IsPartial(classSymbol) || !IsComponent(classDeclaration, classSymbol, compilation))
return;
var globalEnable = compilation.Assembly.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == "Microsoft.AspNetCore.Components.GlobalGenerateSetParametersAsyncAttribute")
?.ConstructorArguments.FirstOrDefault().Value as bool? ?? false;
var hasPositive = classSymbol.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, positiveAttr));
if (!globalEnable && !hasPositive)
return;
GenerateSetParametersAsyncMethod(context, classSymbol);
}
private static void GenerateSetParametersAsyncMethod(SourceProductionContext context, INamedTypeSymbol class_symbol)
{
var force_exact_match = class_symbol.GetAttributes().Any(a => a.NamedArguments.Any(na => na.Key == "RequireExactMatch" && na.Value.Value is bool v && v));
var namespaceName = class_symbol.ContainingNamespace.ToDisplayString();
var type_kind = class_symbol.TypeKind switch { TypeKind.Class => "class", TypeKind.Interface => "interface", _ => "struct" };
var type_parameters = string.Join(", ", class_symbol.TypeArguments.Select(t => t.Name));
type_parameters = string.IsNullOrEmpty(type_parameters) ? type_parameters : "<" + type_parameters + ">";
context.AddCode(class_symbol.ToDisplayString() + "_override.cs", $@"
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
using System.Threading.Tasks;
#pragma warning disable CA1849
#pragma warning disable CA2007
#pragma warning disable CS0162
#pragma warning disable CS8632
namespace {namespaceName}
{{
public partial class {class_symbol.Name}{type_parameters}
{{
private bool _initialized;
/// <summary>
/// <inheritdoc/>
/// </summary>
public override Task SetParametersAsync(ParameterView parameters)
{{
Dictionary<string,object?> parameterValues = new();
foreach (var parameter in parameters)
{{
if(BlazorImplementation__WriteSingleParameter(parameter.Name, parameter.Value)==false)
{{
// 如果没有处理参数,则添加到参数列表中
parameterValues.Add(parameter.Name, parameter.Value);
}}
}}
if(parameterValues.Count > 0)
{{
parameters.SetParameterProperties(this);
}}
if (!_initialized)
{{
_initialized = true;
return RunInitAndSetParametersAsync();
}}
else
{{
return CallOnParametersSetAsync();
}}
}}
// We do not want the debugger to consider NavigationExceptions caught by this method as user-unhandled.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private async Task RunInitAndSetParametersAsync()
{{
Task task;
try
{{
OnInitialized();
task = OnInitializedAsync();
}}
catch (Exception ex) when (ex is not NavigationException)
{{
throw;
}}
if (task.Status != TaskStatus.RanToCompletion && task.Status != TaskStatus.Canceled)
{{
// Call state has changed here so that we render after the sync part of OnInitAsync has run
// and wait for it to finish before we continue. If no async work has been done yet, we want
// to defer calling StateHasChanged up until the first bit of async code happens or until
// the end. Additionally, we want to avoid calling StateHasChanged if no
// async work is to be performed.
StateHasChanged();
try
{{
await task;
}}
catch // avoiding exception filters for AOT runtime support
{{
// Ignore exceptions from task cancellations.
// Awaiting a canceled task may produce either an OperationCanceledException (if produced as a consequence of
// CancellationToken.ThrowIfCancellationRequested()) or a TaskCanceledException (produced as a consequence of awaiting Task.FromCanceled).
// It's much easier to check the state of the Task (i.e. Task.IsCanceled) rather than catch two distinct exceptions.
if (!task.IsCanceled)
{{
throw;
}}
}}
// Don't call StateHasChanged here. CallOnParametersSetAsync should handle that for us.
}}
await CallOnParametersSetAsync();
}}
// We do not want the debugger to consider NavigationExceptions caught by this method as user-unhandled.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private Task CallOnParametersSetAsync()
{{
Task task;
try
{{
OnParametersSet();
task = OnParametersSetAsync();
}}
catch (Exception ex) when (ex is not NavigationException)
{{
#if NET9_0_OR_GREATER
System.Diagnostics.Debugger.BreakForUserUnhandledException(ex);
#endif
throw;
}}
// If no async work is to be performed, i.e. the task has already ran to completion
// or was canceled by the time we got to inspect it, avoid going async and re-invoking
// StateHasChanged at the culmination of the async work.
var shouldAwaitTask = task.Status != TaskStatus.RanToCompletion &&
task.Status != TaskStatus.Canceled;
// We always call StateHasChanged here as we want to trigger a rerender after OnParametersSet and
// the synchronous part of OnParametersSetAsync has run.
StateHasChanged();
return shouldAwaitTask ?
CallStateHasChangedOnAsyncCompletion(task) :
Task.CompletedTask;
}}
// We do not want the debugger to stop more than once per user-unhandled exception.
#if NET9_0_OR_GREATER
[System.Diagnostics.DebuggerDisableUserUnhandledExceptions]
#endif
private async Task CallStateHasChangedOnAsyncCompletion(Task task)
{{
try
{{
await task;
}}
catch // avoiding exception filters for AOT runtime support
{{
// Ignore exceptions from task cancellations, but don't bother issuing a state change.
if (task.IsCanceled)
{{
return;
}}
throw;
}}
StateHasChanged();
}}
}}
}}
#pragma warning restore CS8632
#pragma warning restore CS0162
#pragma warning restore CA2007
#pragma warning restore CA1849
");
var bases = class_symbol.GetTypeHierarchy().Where(t => !SymbolEqualityComparer.Default.Equals(t, class_symbol));
var members = class_symbol.GetMembers() // members of the type itself
.Concat(bases.SelectMany(t => t.GetMembers().Where(m => m.DeclaredAccessibility != Accessibility.Private))) // plus accessible members of any base
.Distinct(SymbolEqualityComparer.Default);
var property_symbols = members.OfType<IPropertySymbol>();
var writable_property_symbols = property_symbols.Where(ps =>
!ps.IsReadOnly || ps.GetAttributes().Any(a =>
a.AttributeClass?.Name is "CascadingParameter" or "CascadingParameterAttribute")
);
var parameter_symbols = writable_property_symbols
.Where(ps => ps.GetAttributes().Any(a => false
|| a.AttributeClass.Name == "Parameter"
|| a.AttributeClass.Name == "ParameterAttribute"
|| a.AttributeClass.Name == "CascadingParameter"
|| a.AttributeClass.Name == "CascadingParameterAttribute"
));
var name_conflicts = parameter_symbols.GroupBy(ps => ps.Name.ToLowerInvariant()).Where(g => g.Count() > 1);
foreach (var conflict in name_conflicts)
{
var key = conflict.Key;
var conflicting_parameters = conflict.ToList();
foreach (var parameter in conflicting_parameters)
{
var this_name = parameter.Name;
var conflicting_name = conflicting_parameters.Select(p => p.Name).FirstOrDefault(n => n != this_name);
foreach (var location in parameter.Locations)
{
context.ReportDiagnostic(Diagnostic.Create(ParameterNameConflict, location, this_name, conflicting_name));
}
}
}
var all = parameter_symbols.ToList();
var catch_all_parameter = parameter_symbols.FirstOrDefault(p =>
{
var parameter_attr = p.GetAttributes().FirstOrDefault(a => a.AttributeClass!.Name.StartsWith("Parameter"));
return parameter_attr?.NamedArguments.Any(n => n.Key == "CaptureUnmatchedValues" && n.Value.Value is bool v && v) == true;
});
var lower_case_match_cases = parameter_symbols.Except(new[] { catch_all_parameter }).Select(p => $"case \"{p.Name.ToLowerInvariant()}\": this.{p.Name} = ({p.Type.ToDisplayString()}) value; break;");
var lower_case_match_default = catch_all_parameter == null ? @"default: {return false;}" : $@"
default:
{{
this.{catch_all_parameter.Name} ??= new System.Collections.Generic.Dictionary<string, object>();
var writable_dict = this.{catch_all_parameter.Name};
if (!writable_dict.TryAdd(name, value))
{{
writable_dict[name] = value;
}}
break;
}}";
var exact_match_cases = parameter_symbols.Except(new[] { catch_all_parameter }).Select(p => $"case \"{p!.Name}\": this.{p.Name} = ({p.Type.ToDisplayString()}) value; break;");
string exact_match_default;
if (force_exact_match)
{
if (catch_all_parameter == null) // exact matches are forced, and we do not have a catch-all parameter, therefore we need to throw on unmatched parameter
{
exact_match_default = @"default: { return false;";
}
else // exact matches are forced, and we DO have a catch-all parameter, therefore we simply add that unmatched parameter to the dictionary
{
exact_match_default = $@"
default:
{{
this.{catch_all_parameter.Name} ??= new System.Collections.Generic.Dictionary<string, object>();
var writable_dict = this.{catch_all_parameter.Name};
if (!writable_dict.TryAdd(name, value))
{{
writable_dict[name] = value;
}}
break;
}}";
}
}
else
{
// exact matches are not forced, so if there is no exact match, we fall back to compare it in lower case
exact_match_default = $@"
default:
{{
switch (name.ToLowerInvariant())
{{
{string.Join("\n", lower_case_match_cases)}
{lower_case_match_default}
}}
break;
}}
";
}
context.AddCode(class_symbol.ToDisplayString() + "_implementation.cs", $@"
using System;
#pragma warning disable CS0162
#pragma warning disable CS0618
#pragma warning disable CS8632
namespace {namespaceName}
{{
public partial class {class_symbol.Name}{type_parameters}
{{
private bool BlazorImplementation__WriteSingleParameter(string name, object value)
{{
if(name != ""Body"")
{{
switch (name)
{{
{string.Join("\n", exact_match_cases)}
{exact_match_default}
}}
return true;
}}
return false;
}}
}}
}}
#pragma warning restore CS8632
#pragma warning restore CS0618
#pragma warning restore CS0162");
}
private static bool IsPartial(INamedTypeSymbol symbol)
{
return symbol.DeclaringSyntaxReferences
.Select(r => r.GetSyntax())
.OfType<ClassDeclarationSyntax>()
.Any(c => c.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)));
}
private static bool IsComponent(ClassDeclarationSyntax classDeclaration, INamedTypeSymbol symbol, Compilation compilation)
{
if (HasUserDefinedSetParametersAsync(symbol))
return false;
if (classDeclaration.SyntaxTree.FilePath.EndsWith(".razor") || classDeclaration.SyntaxTree.FilePath.EndsWith(".razor.cs"))
return true;
var iComponent = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.IComponent");
var componentBase = compilation.GetTypeByMetadataName("Microsoft.AspNetCore.Components.ComponentBase");
if (iComponent == null || componentBase == null)
return false;
return symbol.AllInterfaces.Contains(iComponent) || SymbolEqualityComparer.Default.Equals(symbol.BaseType, componentBase);
}
private static bool HasUserDefinedSetParametersAsync(INamedTypeSymbol classSymbol)
{
return classSymbol
.GetMembers("SetParametersAsync")
.OfType<IMethodSymbol>()
.Any(m =>
m.Parameters.Length == 1 &&
m.Parameters[0].Type.ToDisplayString() == "Microsoft.AspNetCore.Components.ParameterView" &&
m.DeclaredAccessibility == Accessibility.Public &&
!m.IsStatic);
}
}

View File

@@ -0,0 +1,15 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace Microsoft.AspNetCore.Components
{
internal static class SourceGeneratorContextExtension
{
public static void AddCode(this SourceProductionContext context, string hint_name, string code)
{
context.AddSource(hint_name.Replace("<", "_").Replace(">", "_"), SourceText.From(code, Encoding.UTF8));
}
}
}

View File

@@ -0,0 +1,19 @@
using Microsoft.CodeAnalysis;
namespace Microsoft.AspNetCore.Components
{
public static class TypeSymbolExtension
{
public static IEnumerable<INamedTypeSymbol> GetTypeHierarchy(this INamedTypeSymbol symbol)
{
yield return symbol;
if (symbol.BaseType != null)
{
foreach (var type in GetTypeHierarchy(symbol.BaseType))
{
yield return type;
}
}
}
}
}

View File

@@ -0,0 +1,49 @@
param($installPath, $toolsPath, $package, $project)
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
# Install the language agnostic analyzers.
if (Test-Path $analyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Install language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName)
}
}
}
}

View File

@@ -0,0 +1,56 @@
param($installPath, $toolsPath, $package, $project)
$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers" ) * -Resolve
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall the language agnostic analyzers.
if (Test-Path $analyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $analyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
}
}
}
# $project.Type gives the language name like (C# or VB.NET)
$languageFolder = ""
if($project.Type -eq "C#")
{
$languageFolder = "cs"
}
if($project.Type -eq "VB.NET")
{
$languageFolder = "vb"
}
if($languageFolder -eq "")
{
return
}
foreach($analyzersPath in $analyzersPaths)
{
# Uninstall language specific analyzers.
$languageAnalyzersPath = join-path $analyzersPath $languageFolder
if (Test-Path $languageAnalyzersPath)
{
foreach ($analyzerFilePath in Get-ChildItem $languageAnalyzersPath -Filter *.dll)
{
if($project.Object.AnalyzerReferences)
{
try
{
$project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName)
}
catch
{
}
}
}
}
}

21
src/Admin/README.md Normal file
View File

@@ -0,0 +1,21 @@
<div align="center"><h1 align="center">ThingsBlazor</h1></div>
<div align="center"><h3 align="center">权限管理框架</h3></div>
### 🎁 框架介绍
一个通用的AspNetCore小型权限框架BlazorServer实现RBAC权限管理在线会话管理。
### 📙 衍生作品
- 👉[ThingsGateway](https://gitee.com/diego2098/ThingsGateway)
跨平台边缘采集网关
### 💐 特别鸣谢/源码参考(引用)
- 👉[Simple.Admin](https://gitee.com/zxzyjs/SimpleAdmin)
- 👉[Furion](https://gitee.com/dotnetchina/Furion)
- 👉[Bootstrap Blazor](https://www.blazor.zone/)
### 🍎 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

21
src/Admin/README.zh-CN.md Normal file
View File

@@ -0,0 +1,21 @@
<div align="center"><h1 align="center">ThingsBlazor</a></h1></div>
<div align="center"><h3 align="center">权限管理框架</h3></div>
### 🎁 框架介绍
一个通用的AspNetCore小型权限框架BlazorServer实现RBAC权限管理在线会话管理。
### 📙 衍生作品
- 👉[ThingsGateway](https://gitee.com/diego2098/ThingsGateway)
跨平台边缘采集网关
### 💐 特别鸣谢/源码参考(引用)
- 👉[Simple.Admin](https://gitee.com/zxzyjs/SimpleAdmin)
- 👉[Furion](https://gitee.com/dotnetchina/Furion)
- 👉[Bootstrap Blazor](https://www.blazor.zone/)
### 🍎 联系作者
* QQ群605534569
* 邮箱2248356998@qq.com

View File

@@ -0,0 +1,169 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Rougamo;
using Rougamo.Context;
using Rougamo.Metadatas;
using System.Collections.Concurrent;
using ThingsGateway.Extension;
using ThingsGateway.FriendlyException;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// Aop拦截器
/// </summary>
[Pointcut(AccessFlags.Public | AccessFlags.Method)]
[Advice(Feature.OnException | Feature.OnSuccess)]
public sealed class OperDescAttribute : MoAttribute
{
/// <summary>
/// 日志消息队列(线程安全)
/// </summary>
private static readonly ConcurrentQueue<SysOperateLog> _logMessageQueue = new();
private static readonly IAppService AppService;
static OperDescAttribute()
{
AppService = App.RootServices.GetService<IAppService>();
// 创建长时间运行的后台任务,并将日志消息队列中数据写入存储中
Task.Factory.StartNew(ProcessQueueAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
}
public OperDescAttribute(string description, bool isRecordPar = true, object localizerType = null)
{
Description = description;
IsRecordPar = isRecordPar;
LocalizerType = (Type)localizerType;
}
/// <summary>
/// 说明需配置本地化json文件
/// </summary>
public string Description { get; }
/// <summary>
/// 是否记录进出参数
/// </summary>
public bool IsRecordPar { get; }
public Type? LocalizerType { get; }
public override void OnException(MethodContext context)
{
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
{
//插入异常日志
SysOperateLog log = GetOperLog(LocalizerType, context);
log.Category = LogCateGoryEnum.Exception;//操作类型为异常
log.ExeStatus = false;//操作状态为失败
if (context.Exception is AppFriendlyException exception)
log.ExeMessage = exception?.Message;
else
log.ExeMessage = context.Exception?.ToString();
OperDescAttribute.WriteToQueue(log);
}
}
public override void OnSuccess(MethodContext context)
{
if (App.HttpContext?.Request.Path.StartsWithSegments("/_blazor") == true)
{
//插入操作日志
SysOperateLog log = GetOperLog(LocalizerType, context);
OperDescAttribute.WriteToQueue(log);
}
}
private static SqlSugarClient _db = DbContext.GetDB<SysOperateLog>();
/// <summary>
/// 将日志消息写入数据库中
/// </summary>
private static async Task ProcessQueueAsync()
{
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
{
try
{
var data = _logMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据
if (data.Count > 0)
{
await _db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库
}
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false);
}
}
}
private SysOperateLog GetOperLog(Type? localizerType, MethodContext context)
{
var methodBase = context.Method;
var userAgent = AppService.UserAgent;
string? paramJson = null;
if (IsRecordPar)
{
var args = context.Arguments;
var parametersInfo = methodBase.GetParameters();
var parametersDict = new Dictionary<string, object>();
for (int i = 0; i < parametersInfo.Length; i++)
{
parametersDict[parametersInfo[i].Name!] = args[i];
}
paramJson = parametersDict.ToSystemTextJsonString();
}
var result = context.ReturnValue;
var resultJson = IsRecordPar ? result?.ToSystemTextJsonString() : null;
//操作日志表实体
var log = new SysOperateLog
{
Name = (localizerType == null ? App.CreateLocalizerByType(typeof(OperDescAttribute)) : App.CreateLocalizerByType(localizerType))![Description],
Category = LogCateGoryEnum.Operate,
ExeStatus = true,
OpIp = AppService?.RemoteIpAddress ?? string.Empty,
OpBrowser = userAgent?.Browser,
OpOs = userAgent?.Platform,
OpTime = DateTime.Now,
OpAccount = UserManager.UserAccount,
ReqUrl = null,
ReqMethod = "browser",
ResultJson = resultJson,
ClassName = methodBase.ReflectedType!.Name,
MethodName = methodBase.Name,
ParamJson = paramJson,
VerificatId = UserManager.VerificatId,
};
return log;
}
/// <summary>
/// 将日志消息写入队列中等待后台任务出队写入数据库
/// </summary>
/// <param name="logMsg">结构化日志消息</param>
private static void WriteToQueue(SysOperateLog logMsg)
{
_logMessageQueue.Enqueue(logMsg);
}
}

View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 需要角色授权权限
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class RolePermissionAttribute : Attribute
{
}
/// <summary>
/// 忽略角色授权权限
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public sealed class IgnoreRolePermissionAttribute : Attribute
{
}

View File

@@ -0,0 +1,20 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
[AttributeUsage(AttributeTargets.Method)]
public sealed class LoginLogAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public sealed class LogoutLogAttribute : Attribute
{
}

View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 管理员才能访问
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class SuperAdminAttribute : Attribute
{
}
/// <summary>
/// 忽略超级管理员才能访问特性
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class IgnoreSuperAdminAttribute : Attribute
{
}

View File

@@ -0,0 +1,85 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class CacheConst
{
/// <summary>
/// Token表缓存Key
/// </summary>
public const string Cache_HardwareInfo = $"{CacheConst.Cache_Prefix_Admin}Cache_HardwareInfo:";
public const string Cache_Prefix_Admin = "ThingsGatewayAdmin:";
/// <summary>
/// 系统字典表缓存Key
/// </summary>
public const string Cache_SysDict = $"{CacheConst.Cache_Prefix_Admin}SysDict:";
/// <summary>
/// 关系表缓存Key
/// </summary>
public const string Cache_SysRelation = $"{CacheConst.Cache_Prefix_Admin}SysRelation:";
/// <summary>
/// 资源表缓存Key
/// </summary>
public const string Cache_SysResource = $"{CacheConst.Cache_Prefix_Admin}SysResource:List";
/// <summary>
/// 角色表缓存Key
/// </summary>
public const string Cache_SysRole = $"{CacheConst.Cache_Prefix_Admin}SysRole:List";
/// <summary>
/// 用户表缓存Key
/// </summary>
public const string Cache_SysUser = $"{CacheConst.Cache_Prefix_Admin}SysUser:Hash";
/// <summary>
/// 用户账号关系缓存Key
/// </summary>
public const string Cache_SysUserAccount = $"{CacheConst.Cache_Prefix_Admin}SysUserAccount:Hash";
/// <summary>
/// 职位表缓存Key
/// </summary>
public const string Cache_SysPosition = $"{CacheConst.Cache_Prefix_Admin}SysPosition:";
/// <summary>
/// 机构表缓存Key
/// </summary>
public const string Cache_SysOrg = $"{CacheConst.Cache_Prefix_Admin}SysOrg:List";
/// <summary>
/// 公司表缓存Key
/// </summary>
public const string Cache_SysTenant = $"{CacheConst.Cache_Prefix_Admin}Tenant:";
/// <summary>
/// 公司表缓存Key
/// </summary>
public const string Cache_SysOrgTenant = $"{CacheConst.Cache_Prefix_Admin}OrgTenant:Hash";
/// <summary>
/// Token表缓存Key
/// </summary>
public const string Cache_Token = $"{CacheConst.Cache_Prefix_Admin}Token:Hash";
#region
/// <summary>
/// 登录错误次数缓存Key
/// </summary>
public const string Cache_LoginErrorCount = $"{CacheConst.Cache_Prefix_Admin}LoginErrorCount:";
#endregion
}

View File

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

View File

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

View File

@@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class RoleConst
{
/// <summary>
/// api角色
/// </summary>
public const string ApiRole = "ApiRole";
/// <summary>
/// 业务管理员
/// </summary>
public const string BizAdmin = "BizAdmin";
/// <summary>
/// 超级管理员
/// </summary>
public const string SuperAdmin = "SuperAdmin";
/// <summary>
/// 超级管理员Id
/// </summary>
public const long SuperAdminId = 212725263002001;
/// <summary>
/// 默认租户Id
/// </summary>
public const long DefaultTenantId = 252885263003720;
/// <summary>
/// 默认岗位Id
/// </summary>
public const long DefaultPositionId = 212725263003001;
/// <summary>
/// 超级管理员Id
/// </summary>
public const long SuperAdminRoleId = 212725263001001;
}

View File

@@ -0,0 +1,43 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// SqlSugar系统常量
/// </summary>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class SqlSugarConst
{
/// <summary>
/// DB_Admin
/// </summary>
public const string DB_Admin = "DB_Admin";
/// <summary>
/// DB_Custom
/// </summary>
public const string DB_Custom = "DB_Custom";
/// <summary>
/// DB_HardwareInfo
/// </summary>
public const string DB_HardwareInfo = "DB_HardwareInfo";
/// <summary>
/// DB_Log
/// </summary>
public const string DB_Log = "DB_Log";
/// <summary>
/// DB_TokenCache
/// </summary>
public const string DB_TokenCache = "DB_TokenCache";
}

View File

@@ -0,0 +1,57 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.Application;
[ApiDescriptionSettings(false)]
[Route("api/auth")]
[RequestAudit]
public class AuthController : ControllerBase
{
private readonly IAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[HttpPost("login")]
[AllowAnonymous]
[LoginLog]
public Task<LoginOutput> LoginAsync([FromBody] LoginInput input)
{
return _authService.LoginAsync(input);
}
[HttpGet("oauth-login")]
[AllowAnonymous]
[SuppressRequestAudit]
public IActionResult OAuthLogin(string scheme = "Gitee", string returnUrl = "/")
{
var props = new AuthenticationProperties
{
RedirectUri = returnUrl
};
return Challenge(props, scheme);
}
[HttpPost("logout")]
[Authorize]
[IgnoreRolePermission]
[LogoutLog]
public Task LogoutAsync()
{
return _authService.LoginOutAsync();
}
}

View File

@@ -0,0 +1,72 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文化 Controller
/// </summary>
[ApiDescriptionSettings(false)]
[Route("[controller]/[action]")]
public class CultureController : Controller
{
/// <summary>
/// 重置文化方法
/// </summary>
/// <param name="redirectUri"></param>
/// <returns></returns>
[HttpGet]
public IActionResult ResetCulture(string redirectUri)
{
HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
return LocalRedirect(redirectUri);
}
/// <summary>
/// 设置文化方法
/// </summary>
/// <param name="culture"></param>
/// <param name="redirectUri"></param>
/// <returns></returns>
[HttpGet]
public IActionResult SetCulture(string culture, string redirectUri)
{
if (string.IsNullOrEmpty(culture))
{
HttpContext.Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
}
else
{
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture, culture)), new CookieOptions()
{
Expires = DateTimeOffset.Now.AddYears(1)
});
//更改全局文化,采集后台也会变化
//var cultureInfo = new CultureInfo(culture);
//CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
//CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
//CultureInfo.CurrentCulture = cultureInfo;
//CultureInfo.CurrentUICulture = cultureInfo;
}
return LocalRedirect(redirectUri);
}
}

View File

@@ -0,0 +1,53 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 文件下载
/// </summary>
[ApiDescriptionSettings(false)]
[Route("api/file")]
public class FileController : ControllerBase
{
/// <summary>
/// 下载wwwroot文件夹下的文件
/// </summary>
/// <param name="fileName">相对路径</param>
/// <returns></returns>
[HttpGet("download")]
public IActionResult Download(string fileName)
{
// Validate the fileName to prevent path injection
if (string.IsNullOrEmpty(fileName))
{
return BadRequest("Invalid file name.");
}
var root = Directory.GetCurrentDirectory();
var wwwroot = Path.Combine(root, "wwwroot");
var filePath = Path.Combine(wwwroot, fileName);
#pragma warning disable CA3003
if ((!(fileName.StartsWith(@"../Logs") || fileName.StartsWith(@"..\Logs")) && filePath.Contains("..")) || !System.IO.File.Exists(filePath))
{
return NotFound();
}
#pragma warning restore CA3003
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Response.Headers.Append("Access-Control-Expose-Headers", "Content-Disposition");
return File(fileStream, "application/octet-stream", (Path.GetFileName(filePath).Replace('/', '_')));
}
}

View File

@@ -0,0 +1,56 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 登录
/// </summary>
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[Description("登录")]
[Route("openapi/auth")]
[Authorize(AuthenticationSchemes = "Bearer")]
[RequestAudit]
[ApiController]
public class OpenApiController : ControllerBase
{
private readonly IAuthService _authService;
public OpenApiController(IAuthService authService)
{
_authService = authService;
}
[HttpPost("login")]
[DisplayName("登录")]
[AllowAnonymous]
public async Task<OpenApiLoginOutput> LoginAsync([FromBody] OpenApiLoginInput input)
{
var output = await _authService.LoginAsync(input.AdaptLoginInput(), false).ConfigureAwait(false);
var openApiLoginOutput = output.AdaptOpenApiLoginOutput();
return openApiLoginOutput;
}
[HttpPost("logout")]
[Authorize]
[DisplayName("登出")]
[IgnoreRolePermission]
public Task LogoutAsync()
{
return _authService.LoginOutAsync();
}
}

View File

@@ -0,0 +1,55 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
[SugarTable("sys_dict", TableDescription = "字典表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysDict : BaseEntity
{
/// <summary>
/// 类型
///</summary>
[SugarColumn(ColumnDescription = "类型")]
[AutoGenerateColumn(Ignore = true, Filterable = true, Sortable = true)]
public virtual DictTypeEnum DictType { get; set; }
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnDescription = "分类", Length = 200)]
[Required]
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
public string Category { get; set; }
/// <summary>
/// 名称
///</summary>
[SugarColumn(ColumnDescription = "名称", Length = 200)]
[Required]
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
public virtual string Name { get; set; }
/// <summary>
/// 代码
///</summary>
[SugarColumn(ColumnDescription = "代码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
[Required]
[AutoGenerateColumn(Searchable = true, Filterable = true, Sortable = true)]
public virtual string Code { get; set; }
/// <summary>
/// 描述
///</summary>
[SugarColumn(ColumnDescription = "描述", Length = 200, IsNullable = true)]
public string Remark { get; set; }
}

View File

@@ -0,0 +1,132 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 操作日志表
///</summary>
[SugarTable("sys_operatelog", TableDescription = "操作日志表")]
[Tenant(SqlSugarConst.DB_Log)]
public class SysOperateLog
{
/// <summary>
/// 日志分类
///</summary>
[SugarColumn(ColumnDescription = "日志分类")]
[AutoGenerateColumn(Order = 1, Filterable = true, Sortable = true)]
public LogCateGoryEnum Category { get; set; }
/// <summary>
/// 日志名称
///</summary>
[SugarColumn(ColumnDescription = "日志名称", Length = 200)]
[AutoGenerateColumn(Order = 2, Filterable = true, Sortable = true)]
public string Name { get; set; }
/// <summary>
/// 类名称
///</summary>
[SugarColumn(ColumnDescription = "类名称", Length = 200)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string ClassName { get; set; }
/// <summary>
/// 方法名称
///</summary>
[SugarColumn(ColumnDescription = "方法名称", Length = 200)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string MethodName { get; set; }
/// <summary>
/// 请求参数
///</summary>
[SugarColumn(ColumnDescription = "请求参数", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
public string? ParamJson { get; set; }
/// <summary>
/// 请求方式
///</summary>
[SugarColumn(ColumnDescription = "请求方式", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string? ReqMethod { get; set; }
/// <summary>
/// 请求地址
///</summary>
[SugarColumn(ColumnDescription = "请求地址", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string? ReqUrl { get; set; }
/// <summary>
/// 返回结果
///</summary>
[SugarColumn(ColumnDescription = "返回结果", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
public string? ResultJson { get; set; }
/// <summary>
/// 具体消息
///</summary>
[SugarColumn(ColumnDescription = "具体消息", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(ShowTips = true, Filterable = true, Sortable = true)]
public string? ExeMessage { get; set; }
/// <summary>
/// 执行状态
///</summary>
[SugarColumn(ColumnDescription = "执行状态")]
[AutoGenerateColumn(Filterable = true, Sortable = true)]
public bool ExeStatus { get; set; }
/// <summary>
/// 操作账号
///</summary>
[SugarColumn(ColumnDescription = "操作账号", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Filterable = true, Sortable = true)]
public string? OpAccount { get; set; }
/// <summary>
/// 操作浏览器
///</summary>
[SugarColumn(ColumnDescription = "操作浏览器", Length = 200)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string OpBrowser { get; set; }
/// <summary>
/// 操作ip
///</summary>
[SugarColumn(ColumnDescription = "操作ip", Length = 200)]
[AutoGenerateColumn(Filterable = true, Sortable = true)]
public string? OpIp { get; set; }
/// <summary>
/// 操作系统
///</summary>
[SugarColumn(ColumnDescription = "操作系统", Length = 200)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public string OpOs { get; set; }
/// <summary>
/// 操作时间
///</summary>
[SugarColumn(ColumnDescription = "操作时间")]
[AutoGenerateColumn(Visible = true, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Desc)]
public DateTime OpTime { get; set; }
/// <summary>
/// 验证Id
///</summary>
[SugarColumn(ColumnDescription = "验证Id")]
[IgnoreExcel]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public long VerificatId { get; set; }
}

View File

@@ -0,0 +1,74 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 组织表
///</summary>
[SugarTable("sys_org", TableDescription = "组织表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysOrg : BaseEntity
{
/// <summary>
/// 父id
///</summary>
[SugarColumn(ColumnName = "ParentId", ColumnDescription = "父id")]
[AutoGenerateColumn(Ignore = true)]
public long ParentId { get; set; }
[SugarColumn(ColumnName = "ParentIdList", ColumnDescription = "父id列表", IsNullable = true, IsJson = true)]
[AutoGenerateColumn(Ignore = true)]
public List<long> ParentIdList { get; set; } = new List<long>();
/// <summary>
/// 主管ID
///</summary>
[SugarColumn(ColumnName = "DirectorId", ColumnDescription = "主管ID", IsNullable = true)]
[AutoGenerateColumn(Ignore = true)]
public long? DirectorId { get; set; }
/// <summary>
/// 名称
///</summary>
[SugarColumn(ColumnName = "Name", ColumnDescription = "名称", Length = 200)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
[Required]
public string Name { get; set; }
/// <summary>
/// 全称
///</summary>
[SugarColumn(ColumnName = "Names", ColumnDescription = "全称", Length = 500)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string Names { get; set; }
/// <summary>
/// 编码
///</summary>
[SugarColumn(ColumnName = "Code", ColumnDescription = "编码", Length = 200)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string Code { get; set; }
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnName = "Category", ColumnDescription = "分类")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public OrgEnum Category { get; set; }
[SugarColumn(ColumnName = "Status", ColumnDescription = "启用")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public bool Status { get; set; } = true;
}

View File

@@ -0,0 +1,55 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 职位表
///</summary>
[SugarTable("sys_position", TableDescription = "职位表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysPosition : BaseEntity
{
/// <summary>
/// 组织id
///</summary>
[SugarColumn(ColumnName = "OrgId", ColumnDescription = "组织id")]
[AutoGenerateColumn(Ignore = true)]
[MinValue(1)]
public virtual long OrgId { get; set; }
/// <summary>
/// 名称
///</summary>
[SugarColumn(ColumnName = "Name", ColumnDescription = "名称", Length = 200)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
[Required]
public virtual string Name { get; set; }
/// <summary>
/// 编码
///</summary>
[SugarColumn(ColumnName = "Code", ColumnDescription = "编码", Length = 200)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string Code { get; set; }
[SugarColumn(ColumnName = "Status", ColumnDescription = "启用")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public bool Status { get; set; } = true;
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnName = "Category", ColumnDescription = "分类")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual PositionCategoryEnum Category { get; set; }
}

View File

@@ -0,0 +1,37 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统关系表
///</summary>
[SugarTable("sys_relation", TableDescription = "系统关系表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysRelation : PrimaryKeyEntity
{
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnDescription = "分类")]
public RelationCategoryEnum Category { get; set; }
/// <summary>
/// 对象ID
///</summary>
[SugarColumn(ColumnDescription = "对象ID")]
public long ObjectId { get; set; }
/// <summary>
/// 目标ID
///</summary>
[SugarColumn(ColumnDescription = "目标ID", IsNullable = true)]
public string? TargetId { get; set; }
}

View File

@@ -0,0 +1,97 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Routing;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统资源表
///</summary>
[SugarTable("sys_resource", TableDescription = "系统资源表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysResource : BaseEntity
{
/// <summary>
/// 父id
///</summary>
[SugarColumn(ColumnDescription = "父id")]
[AutoGenerateColumn(Ignore = true)]
public virtual long ParentId { get; set; } = 0;
/// <summary>
/// 模块
///</summary>
[SugarColumn(ColumnDescription = "模块")]
[AutoGenerateColumn(Ignore = true)]
public virtual long Module { get; set; }
/// <summary>
/// 标题
///</summary>
[SugarColumn(ColumnDescription = "标题", Length = 200)]
[Required]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)]
public virtual string Title { get; set; }
/// <summary>
/// 图标
///</summary>
[SugarColumn(ColumnDescription = "图标", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
public virtual string? Icon { get; set; }
/// <summary>
/// 编码
///</summary>
[SugarColumn(ColumnDescription = "编码", Length = 200)]
[Required]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public virtual string Code { get; set; }
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnDescription = "分类")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public ResourceCategoryEnum Category { get; set; } = ResourceCategoryEnum.Menu;
/// <summary>
/// 目标类型
///</summary>
[SugarColumn(ColumnDescription = "目标类型", IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual TargetEnum? Target { get; set; }
/// <summary>
/// 菜单匹配类型
/// </summary>
[SugarColumn(ColumnDescription = "菜单匹配类型", IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual NavLinkMatch? NavLinkMatch { get; set; }
/// <summary>
/// 路径
///</summary>
[SugarColumn(ColumnDescription = "路径", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true, Searchable = true)]
public virtual string Href { get; set; }
/// <summary>
/// 子节点
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public List<SysResource>? Children { get; set; }
}

View File

@@ -0,0 +1,88 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统角色表
///</summary>
[SugarTable("sys_role", TableDescription = "系统角色表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysRole : BaseEntity
{
/// <summary>
/// 名称
///</summary>
[SugarColumn(ColumnDescription = "名称", Length = 200)]
[Required]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual string Name { get; set; }
/// <summary>
/// 编码
///</summary>
[SugarColumn(ColumnDescription = "编码", Length = 200)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string Code { get; set; }
/// <summary>
/// 分类
///</summary>
[SugarColumn(ColumnDescription = "分类", IsNullable = false)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual RoleCategoryEnum Category { get; set; }
/// <summary>
/// 组织id
///</summary>
[SugarColumn(ColumnName = "OrgId", ColumnDescription = "组织id", IsNullable = false)]
[AutoGenerateColumn(Ignore = true)]
public long OrgId { get; set; }
/// <summary>
/// 默认数据范围
///</summary>
[SugarColumn(ColumnName = "DefaultDataScope", ColumnDescription = "默认数据范围", IsJson = true, ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = false)]
[AutoGenerateColumn(Ignore = true)]
public virtual DefaultDataScope DefaultDataScope { get; set; } = new();
public override bool Equals(object? obj)
{
if (obj == null || !(obj is SysRole))
{
return false;
}
return Id == ((SysRole)obj).Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
/// <summary>
/// 默认数据范围
/// </summary>
public class DefaultDataScope
{
/// <summary>
/// 数据范围
/// </summary>
public DataScopeEnum ScopeCategory { get; set; }
/// <summary>
/// 自定义机构范围列表
/// </summary>
public List<long> ScopeDefineOrgIdList { get; set; } = new List<long>();
}

View File

@@ -0,0 +1,247 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Riok.Mapperly.Abstractions;
using System.ComponentModel.DataAnnotations;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统用户表
///</summary>
[SugarTable("sys_user", TableDescription = "系统用户表")]
[Tenant(SqlSugarConst.DB_Admin)]
public class SysUser : BaseEntity
{
/// <summary>
/// 头像
///</summary>
[SugarColumn(ColumnDescription = "头像", ColumnDataType = StaticConfig.CodeFirst_BigString, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = false, Filterable = false)]
[MapperIgnore]
public virtual string? Avatar { get; set; }
/// <summary>
/// 账号
///</summary>
[SugarColumn(ColumnDescription = "账号", Length = 200)]
[Required]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public virtual string Account { get; set; }
/// <summary>
/// 密码
///</summary>
[SugarColumn(ColumnDescription = "密码", ColumnDataType = StaticConfig.CodeFirst_BigString)]
[AutoGenerateColumn(Ignore = true)]
public string Password { get; set; }
/// <summary>
/// 状态
///</summary>
[SugarColumn(ColumnDescription = "状态")]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public bool Status { get; set; } = true;
/// <summary>
/// 手机
/// 这里使用了SM4自动加密解密
///</summary>
[SugarColumn(ColumnDescription = "手机", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string? Phone { get; set; }
/// <summary>
/// 邮箱
///</summary>
[SugarColumn(ColumnDescription = "邮箱", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = true, Sortable = true, Filterable = true)]
public string? Email { get; set; }
/// <summary>
/// 上次登录ip
///</summary>
[SugarColumn(ColumnDescription = "上次登录ip", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string? LastLoginIp { get; set; }
/// <summary>
/// 上次登录设备
///</summary>
[SugarColumn(ColumnDescription = "上次登录设备", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string? LastLoginDevice { get; set; }
/// <summary>
/// 上次登录时间
///</summary>
[SugarColumn(ColumnDescription = "上次登录时间", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public DateTime? LastLoginTime { get; set; }
/// <summary>
/// 上次登录地点
///</summary>
[SugarColumn(ColumnDescription = "上次登录地点", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string LastLoginAddress { get; set; }
/// <summary>
/// 最新登录ip
///</summary>
[SugarColumn(ColumnDescription = "最新登录ip", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string? LatestLoginIp { get; set; }
/// <summary>
/// 最新登录时间
///</summary>
[SugarColumn(ColumnDescription = "最新登录时间", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public DateTime? LatestLoginTime { get; set; }
/// <summary>
/// 最新登录设备
///</summary>
[SugarColumn(ColumnDescription = "最新登录设备", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string? LatestLoginDevice { get; set; }
/// <summary>
/// 最新登录地点
///</summary>
[SugarColumn(ColumnDescription = "最新登录地点", Length = 200, IsNullable = true)]
[AutoGenerateColumn(Visible = false, Sortable = true, Filterable = true, IsVisibleWhenAdd = false, IsVisibleWhenEdit = false)]
public string LatestLoginAddress { get; set; }
/// <summary>
/// 机构id
///</summary>
[SugarColumn(ColumnName = "OrgId", ColumnDescription = "机构id", IsNullable = false)]
[AutoGenerateColumn(Ignore = true)]
public virtual long OrgId { get; set; }
/// <summary>
/// 职位id
///</summary>
[SugarColumn(ColumnName = "PositionId", ColumnDescription = "职位id", IsNullable = true)]
[AutoGenerateColumn(Ignore = true)]
[Required]
[NotNull]
public virtual long? PositionId { get; set; }
/// <summary>
/// 主管id
///</summary>
[SugarColumn(ColumnName = "DirectorId", ColumnDescription = "主管id", IsNullable = true)]
[AutoGenerateColumn(Ignore = true)]
public long? DirectorId { get; set; }
#region other
/// <summary>
/// 机构信息
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public string OrgName { get; set; }
/// <summary>
/// 机构信息全称
/// </summary>
[SugarColumn(IsIgnore = true)]
public string OrgNames { get; set; }
/// <summary>
/// 职位信息
/// </summary>
[SugarColumn(IsIgnore = true)]
public string PositionName { get; set; }
/// <summary>
/// 组织和机构ID列表,组织ID从上到下最后是职位
/// </summary>
[SugarColumn(IsIgnore = true, IsJson = true)]
[AutoGenerateColumn(Ignore = true)]
public List<long>? OrgAndPosIdList { get; set; } = new List<long>();
/// <summary>
/// 主管信息
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public UserSelectorOutput? DirectorInfo { get; set; }
#endregion
#region other
/// <summary>
/// 按钮码集合
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public Dictionary<string, List<string>>? ButtonCodeList { get; set; } = new();
/// <summary>
/// 权限码集合
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public HashSet<string>? PermissionCodeList { get; set; } = new();
/// <summary>
/// 角色ID集合
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public HashSet<long>? RoleIdList { get; set; } = new();
/// <summary>
/// 机构及以下机构ID集合
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public HashSet<long>? ScopeOrgChildList { get; set; }
/// <summary>
/// 模块集合
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public List<SysResource>? ModuleList { get; set; } = new();
/// <summary>
/// 租户Id
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public long? TenantId { get; set; }
/// <summary>
/// 全局用戶
/// </summary>
[SugarColumn(IsIgnore = true)]
[AutoGenerateColumn(Ignore = true)]
public bool IsGlobal { get; set; }
#endregion other
}
/// <summary>
/// 数据范围类
/// </summary>
public class DataScope
{
/// <summary>
/// API接口
/// </summary>
public string ApiUrl { get; set; }
}

View File

@@ -0,0 +1,84 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Common.List;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 会话信息
/// </summary>
[SugarTable("verificatinfo", TableDescription = "验证缓存表")]
[Tenant(SqlSugarConst.DB_TokenCache)]
public class VerificatInfo : PrimaryIdEntity
{
/// <summary>
/// 客户端ID列表
/// </summary>
[AutoGenerateColumn(Ignore = true)]
[SugarColumn(ColumnDescription = "客户端ID列表", IsNullable = true, IsJson = true)]
public ConcurrentList<string> ClientIds { get; set; } = new();
/// <summary>
/// 验证Id
/// </summary>
[AutoGenerateColumn(Ignore = true)]
public long UserId { get; set; }
/// <summary>
/// 验证Id
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true)]
[SugarColumn(ColumnDescription = "Id", IsPrimaryKey = true)]
[IgnoreExcel]
[System.ComponentModel.DataAnnotations.Key]
public override long Id { get; set; }
/// <summary>
/// 登录IP
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)]
[SugarColumn(IsNullable = true)]
public string LoginIp { get; set; }
/// <summary>
/// 登录时间
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 200)]
public DateTime LoginTime { get; set; }
/// <summary>
/// 在线状态
/// </summary>
[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 120)]
[SugarColumn(IsIgnore = true)]
public bool Online => ClientIds.Count > 0;
/// <summary>
/// 过期时间
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true)]
public int Expire { get; set; }
/// <summary>
/// 超时时间
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true)]
public DateTime VerificatTimeout { get; set; }
/// <summary>
/// 登录设备
/// </summary>
[AutoGenerateColumn(Filterable = true, Sortable = true, Width = 100)]
[SugarColumn(IsNullable = true)]
public string Device { get; set; }
}

View File

@@ -0,0 +1,39 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum DataScopeEnum
{
/// <summary>
/// 仅自己
/// </summary>
SCOPE_SELF,
/// <summary>
/// 所有
/// </summary>
SCOPE_ALL,
/// <summary>
/// 仅所属组织
/// </summary>
SCOPE_ORG,
/// <summary>
/// 所属组织及以下
/// </summary>
SCOPE_ORG_CHILD,
/// <summary>
/// 自定义
/// </summary>
SCOPE_ORG_DEFINE,
}

View File

@@ -0,0 +1,27 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 字典表类型
/// </summary>
public enum DictTypeEnum
{
/// <summary>
/// 系统使用
/// </summary>
System,
/// <summary>
/// 用户自定义
/// </summary>
Define,
}

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum LogCateGoryEnum
{
Login,
Logout,
Operate,
Exception
}

View File

@@ -0,0 +1,17 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum OrgEnum
{
DEPT,
COMPANY,
}

View File

@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum PositionCategoryEnum
{
HIGH,
MIDDLE,
LOW,
}

View File

@@ -0,0 +1,25 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum RelationCategoryEnum
{
UserHasRole,
UserHasResource,
UserHasPermission,
UserHasOpenApiPermission,
UserHasModule,
UserWorkbenchData,
RoleHasResource,
RoleHasPermission,
RoleHasOpenApiPermission,
RoleHasModule,
}

View File

@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum ResourceCategoryEnum
{
Module,
Menu,
Button,
}

View File

@@ -0,0 +1,17 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum RoleCategoryEnum
{
Global = 0,
Org = 2,
}

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public enum TargetEnum
{
_self,
_blank,
_parent,
_top
}

View File

@@ -0,0 +1,55 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using System.Net;
using System.Text.Json;
namespace Microsoft.AspNetCore.Builder;
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseBootstrapBlazor(this IApplicationBuilder builder)
{
// 获得客户端 IP 地址
builder.UseWhen(context => context.Request.Path.StartsWithSegments("/ip.axd"), app => app.Run(async context =>
{
var ip = "";
var headers = context.Request.Headers;
if (headers.TryGetValue("X-Forwarded-For", out var value))
{
var ips = new List<string>();
foreach (var xf in value)
{
if (!string.IsNullOrEmpty(xf))
{
ips.Add(xf);
}
}
ip = string.Join(";", ips);
}
else
{
ip = context.Connection.RemoteIpAddress.ToIPv4String();
}
context.Response.Headers.TryAdd("Content-Type", new Microsoft.Extensions.Primitives.StringValues("application/json; charset=utf-8"));
await context.Response.WriteAsync(JsonSerializer.Serialize(new { Id = context.TraceIdentifier, Ip = ip })).ConfigureAwait(false);
}));
return builder;
}
public static string ToIPv4String(this IPAddress? address)
{
var ipv4Address = (address ?? IPAddress.IPv6Loopback).ToString();
return ipv4Address.StartsWith("::ffff:") ? (address ?? IPAddress.IPv6Loopback).MapToIPv4().ToString() : ipv4Address;
}
}

View File

@@ -0,0 +1,88 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Configuration;
using Microsoft.Net.Http.Headers;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class CacheExtensions
{
private static int? _age;
private static List<string>? _files;
public static void ProcessCache(this StaticFileResponseContext context, IConfiguration configuration)
{
if (context.CanCache(configuration, out var age))
{
context.Context.Response.Headers[HeaderNames.CacheControl] = $"public, max-age={age}";
}
}
private static bool CanCache(this StaticFileResponseContext context, IConfiguration configuration, out int age)
{
var ret = false;
age = 0;
var files = configuration.GetFiles();
if (files.Any(i => context.CanCache(i)))
{
ret = true;
age = configuration.GetAge();
}
return ret;
}
private static bool CanCache(this StaticFileResponseContext context, string file)
{
var ext = Path.GetExtension(context.File.PhysicalPath) ?? "";
bool ret = file.Equals(ext, StringComparison.OrdinalIgnoreCase);
if (ret && ext.Equals(".js", StringComparison.OrdinalIgnoreCase))
{
// process javascript file
ret = false;
if (context.Context.Request.QueryString.HasValue)
{
var paras = QueryHelpers.ParseQuery(context.Context.Request.QueryString.Value);
ret = paras.ContainsKey("v");
}
}
return ret;
}
private static int GetAge(this IConfiguration configuration)
{
_age ??= GetAge();
return _age.Value;
int GetAge()
{
var cacheSection = configuration.GetSection("Cache-Control");
return cacheSection.GetValue<int>("Max-Age", 1000 * 60 * 10);
}
}
private static List<string> GetFiles(this IConfiguration configuration)
{
_files ??= GetFiles();
return _files;
List<string> GetFiles()
{
var cacheSection = configuration.GetSection("Cache-Control");
return cacheSection.GetSection("Files").Get<List<string>>() ?? new();
}
}
}

View File

@@ -0,0 +1,35 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Extension;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class SchemeHelper
{
public static string GetOrCreate()
{
var path = "Keys/SchemeKey.txt";
if (File.Exists(path))
{
var data = File.ReadAllText(path);
return data;
}
else
{
var data = DateTime.UtcNow.ToDefaultDateTimeFormat();
Directory.CreateDirectory("Keys");
File.WriteAllText(path, data);
return data;
}
}
}

View File

@@ -0,0 +1,15 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace System.Logging;
public class RequestAudit
{
}

View File

@@ -0,0 +1,9 @@

using ThingsGateway.DependencyInjection;
namespace System;
[SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class RequestAuditAttribute : Attribute
{
}

View File

@@ -0,0 +1,99 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.Reflection;
namespace ThingsGateway.Admin.Application;
public class RequestAuditData
{
/// <summary>
/// 分类
/// </summary>
public string CateGory { get; set; }
/// <summary>
/// 客户端信息
/// </summary>
public UserAgent Client { get; set; }
/// <summary>
/// 请求方法POST/GET
/// </summary>
public string Method { get; set; }
/// <summary>
/// 操作名称
/// </summary>
public string Operation { get; set; }
/// <summary>
/// 请求地址
/// </summary>
public string Path { get; set; }
/// <summary>
/// 方法名称
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// 认证信息
/// </summary>
public List<AuthorizationClaims> AuthorizationClaims { get; set; }
/// <summary>
/// 控制器名
/// </summary>
public string ControllerName { get; set; }
/// <summary>
/// 异常信息
/// </summary>
public LogException Exception { get; set; }
public long TimeOperationElapsedMilliseconds { get; set; }
/// <summary>
/// 服务端
/// </summary>
public string LocalIPv4 { get; set; }
/// <summary>
/// 日志时间
/// </summary>
public DateTimeOffset LogDateTime { get; set; }
/// <summary>
/// 参数列表
/// </summary>
public List<Parameters> Parameters { get; set; }
/// <summary>
/// 客户端IPV4地址
/// </summary>
public string RemoteIPv4 { get; set; }
/// <summary>
/// 请求地址
/// </summary>
public string RequestUrl { get; set; }
/// <summary>
/// 返回信息
/// </summary>
public object ReturnInformation { get; set; }
/// <summary>
/// 验证错误信息
/// </summary>
public Validation Validation { get; set; }
public MethodInfo MethodInfo { get; set; }
}

View File

@@ -0,0 +1,289 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Logging;
using ThingsGateway.FriendlyException;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.Json.Extension;
using ThingsGateway.UnifyResult;
namespace ThingsGateway.Admin.Application;
public class RequestAuditFilter : IAsyncActionFilter, IOrderedFilter
{
private const int FilterOrder = -3000;
public int Order => FilterOrder;
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var timeOperation = Stopwatch.StartNew();
var resultContext = await next().ConfigureAwait(false);
// 计算接口执行时间
timeOperation.Stop();
var controllerActionDescriptor = (context.ActionDescriptor as ControllerActionDescriptor);
// 获取动作方法描述器
var actionMethod = controllerActionDescriptor?.MethodInfo;
// 处理 Blazor Server
if (actionMethod == null)
{
return;
}
// 排除 WebSocket 请求处理
if (context.HttpContext.IsWebSocketRequest())
{
return;
}
// 如果贴了 [SuppressMonitor] 特性则跳过
if (actionMethod.IsDefined(typeof(SuppressRequestAuditAttribute), true)
|| actionMethod.DeclaringType.IsDefined(typeof(SuppressRequestAuditAttribute), true))
{
return;
}
// 只有方法贴有特性才进行审计
if (
!actionMethod.DeclaringType.IsDefined(typeof(RequestAuditAttribute), true)
&&
!actionMethod.IsDefined(typeof(RequestAuditAttribute), true))
{
return;
}
var logData = new RequestAuditData();
logData.TimeOperationElapsedMilliseconds = timeOperation.ElapsedMilliseconds;
var resultHttpContext = (resultContext as Microsoft.AspNetCore.Mvc.Filters.FilterContext).HttpContext;
// 获取 HttpContext 和 HttpRequest 对象
var httpContext = context.HttpContext;
var httpRequest = httpContext.Request;
// 获取客户端 Ipv4 地址
var remoteIPv4 = httpContext.GetRemoteIpAddressToIPv4();
logData.RemoteIPv4 = remoteIPv4;
var requestUrl = Uri.UnescapeDataString(httpRequest.GetRequestUrlAddress());
logData.RequestUrl = requestUrl;
object returnValue = null;
Type finalReturnType;
var result = resultContext.Result as IActionResult;
// 解析返回值
if (UnifyContext.CheckVaildResult(result, out var data))
{
returnValue = data;
finalReturnType = data?.GetType();
}
// 处理文件类型
else if (result is FileResult fresult)
{
returnValue = new
{
FileName = fresult.FileDownloadName,
fresult.ContentType,
Length = fresult is FileContentResult cresult ? (object)cresult.FileContents.Length : null
};
finalReturnType = fresult?.GetType();
}
else finalReturnType = result?.GetType();
logData.ReturnInformation = returnValue;
//获取客户端信息
var client = App.GetService<IAppService>().UserAgent;
//操作名称默认是控制器名加方法名,自定义操作名称要在action上加Description特性
var option = $"{controllerActionDescriptor.ControllerName}/{controllerActionDescriptor.ActionName}";
var desc = App.CreateLocalizerByType(controllerActionDescriptor.ControllerTypeInfo.AsType())[actionMethod.Name];
//获取特性
logData.CateGory = desc.Value;//传操作名称
logData.Operation = desc.Value;//传操作名称
logData.Client = client;
logData.Path = httpContext.Request.Path.Value;//请求地址
logData.Method = httpContext.Request.Method;//请求方法
logData.MethodInfo = actionMethod;//请求方法
logData.ControllerName = controllerActionDescriptor.ControllerName;
logData.ActionName = controllerActionDescriptor.ActionName;
logData.AuthorizationClaims = new();
// 获取授权用户
var user = httpContext.User;
foreach (var claim in user.Claims)
{
logData.AuthorizationClaims.Add(new AuthorizationClaims
{
Type = claim.Type,
Value = claim.Value,
});
}
logData.LocalIPv4 = httpContext.GetLocalIpAddressToIPv4();
logData.LogDateTime = DateTimeOffset.Now;
var parameterValues = context.ActionArguments;
logData.Parameters = new();
var parameters = actionMethod.GetParameters();
foreach (var parameter in parameters)
{
// 判断是否禁用记录特定参数
if (parameter.IsDefined(typeof(SuppressRequestAuditAttribute), false)) continue;
// 排除标记 [FromServices] 的解析
if (parameter.IsDefined(typeof(FromServicesAttribute), false)) continue;
var name = parameter.Name;
var parameterType = parameter.ParameterType;
_ = parameterValues.TryGetValue(name, out var value);
var par = new Parameters()
{
Name = name,
};
logData.Parameters.Add(par);
object rawValue = default;
// 文件类型参数
if (value is IFormFile || value is List<IFormFile>)
{
// 单文件
if (value is IFormFile formFile)
{
var fileSize = Math.Round(formFile.Length / 1024D);
rawValue = new
{
name = formFile.Name,
fileName = formFile.FileName,
length = formFile.Length,
contentType = formFile.ContentType
};
}
// 多文件
else if (value is List<IFormFile> formFiles)
{
var rawValues1 = new List<object>();
for (var i = 0; i < formFiles.Count; i++)
{
var file = formFiles[i];
var size = Math.Round(file.Length / 1024D);
var rawValue1 = new
{
name = file.Name,
fileName = file.FileName,
length = file.Length,
contentType = file.ContentType
};
rawValues1.Add(rawValue1);
}
rawValue = rawValues1;
}
}
// 处理 byte[] 参数类型
else if (value is byte[] byteArray)
{
rawValue = new
{
length = byteArray.Length,
};
}
// 处理基元类型,字符串类型和空值
else if (parameterType.IsPrimitive || value is string || value == null)
{
rawValue = value;
}
// 其他类型统一进行序列化
else
{
rawValue = value;
}
par.Value = rawValue;
}
// 获取异常对象情况
Exception exception = resultContext.Exception;
if (exception is AppFriendlyException friendlyException)
{
logData.Validation = new();
logData.Validation.Message = friendlyException.Message;
}
else if (exception != null)
{
logData.Exception = new();
logData.Exception.Message = exception.Message;
logData.Exception.StackTrace = exception.StackTrace;
logData.Exception.Type = HandleGenericType(exception.GetType());
}
// 创建日志记录器
var logger = httpContext.RequestServices.GetRequiredService<ILogger<RequestAudit>>();
var logContext = new LogContext();
logContext.Set(nameof(RequestAuditData), logData);
// 设置日志上下文
using var scope = logger.ScopeContext(logContext);
if (exception == null)
{
if (logger.IsEnabled(LogLevel.Information))
logger.Log(LogLevel.Information, $"{logData.Method}:{logData.Path}-{logData.Operation}");
}
else
{
if (logger.IsEnabled(LogLevel.Warning))
logger.Log(LogLevel.Warning, $"{logData.Method}:{logData.Path}-{logData.Operation}{Environment.NewLine}{logData.Exception?.ToSystemTextJsonString()}{Environment.NewLine}{logData.Validation?.ToSystemTextJsonString()}");
}
}
/// <summary>
/// 处理泛型类型转字符串打印问题
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private static string HandleGenericType(Type type)
{
if (type == null) return string.Empty;
var typeName = type.FullName ?? (!string.IsNullOrEmpty(type.Namespace) ? type.Namespace + "." : string.Empty) + type.Name;
// 处理泛型类型问题
if (type.IsConstructedGenericType)
{
var prefix = type.GetGenericArguments()
.Select(genericArg => HandleGenericType(genericArg))
.Aggregate((previous, current) => previous + ", " + current);
typeName = typeName.Split('`').First() + "<" + prefix + ">";
}
return typeName;
}
}

View File

@@ -0,0 +1,9 @@

using ThingsGateway.DependencyInjection;
namespace System;
[SuppressSniffer, AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class SuppressRequestAuditAttribute : Attribute
{
}

View File

@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
global using BootstrapBlazor.Components;
global using Microsoft.Extensions.Localization;
global using Microsoft.Extensions.Options;
global using System.Diagnostics.CodeAnalysis;
global using System.Globalization;
global using ThingsGateway.Common;
global using ThingsGateway.DB;
global using ThingsGateway.NewLife.Extension;
global using ThingsGateway.SqlSugar;

View File

@@ -0,0 +1,58 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class HardwareInfo
{
/// <summary>
/// 当前磁盘信息
/// </summary>
public DriveInfo DriveInfo { get; set; }
/// <summary>
/// 硬件信息获取
/// </summary>
public MachineInfo? MachineInfo { get; set; }
/// <summary>
/// 主机环境
/// </summary>
public string Environment { get; set; }
/// <summary>
/// NET框架
/// </summary>
public string FrameworkDescription { get; set; }
/// <summary>
/// 系统架构
/// </summary>
public string OsArchitecture { get; set; }
/// <summary>
/// 唯一编码
/// </summary>
public string UUID { get; set; }
/// <summary>
/// 进程占用内存
/// </summary>
[AutoGenerateColumn(Ignore = true)]
public int WorkingSet { get; set; }
/// <summary>
/// 更新时间
/// </summary>
public string UpdateTime { get; set; }
}

View File

@@ -0,0 +1,156 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
using ThingsGateway.Extension;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Caching;
using ThingsGateway.NewLife.Threading;
using ThingsGateway.Schedule;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 获取硬件信息作业任务
/// </summary>
[JobDetail("hardware_log", Description = "获取硬件信息", GroupName = "Hardware", Concurrent = false)]
[PeriodSeconds(30, TriggerId = "trigger_hardware", Description = "获取硬件信息", RunOnStart = true)]
public class HardwareJob : IJob, IHardwareJob
{
private readonly ILogger _logger;
private readonly IStringLocalizer _localizer;
/// <inheritdoc/>
public HardwareJob(ILogger<HardwareJob> logger, IStringLocalizer<HardwareJob> localizer, IOptions<HardwareInfoOptions> options)
{
_logger = logger;
_localizer = localizer;
HardwareInfoOptions = options.Value;
}
#region
/// <summary>
/// 运行信息获取
/// </summary>
public HardwareInfo HardwareInfo { get; } = new();
/// <inheritdoc/>
public HardwareInfoOptions HardwareInfoOptions { get; private set; }
#endregion
private ICache MemoryCache => App.CacheService;
private const string CacheKey = $"{CacheConst.Cache_HardwareInfo}HistoryHardwareInfo";
/// <inheritdoc/>
public async Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos()
{
var historyHardwareInfos = MemoryCache.Get<List<HistoryHardwareInfo>>(CacheKey);
if (historyHardwareInfos == null)
{
using var db = DbContext.GetDB<HistoryHardwareInfo>();
historyHardwareInfos = await db.Queryable<HistoryHardwareInfo>().Where(a => a.Date > DateTime.Now.AddDays(-3)).Take(1000).ToListAsync().ConfigureAwait(false);
MemoryCache.Set(CacheKey, historyHardwareInfos);
}
return historyHardwareInfos;
}
private bool error = false;
private DateTime hisInsertTime = default;
private SqlSugarClient _db = DbContext.GetDB<HistoryHardwareInfo>();
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
if (HardwareInfoOptions.Enable)
{
try
{
if (HardwareInfo.MachineInfo == null)
{
HardwareInfo.MachineInfo = MachineInfo.GetCurrent();
string currentPath = Directory.GetCurrentDirectory();
DriveInfo drive = new(Path.GetPathRoot(currentPath));
HardwareInfoOptions.DaysAgo = Math.Min(Math.Max(HardwareInfoOptions.DaysAgo, 1), 7);
if (HardwareInfoOptions.HistoryInterval < 60000) HardwareInfoOptions.HistoryInterval = 60000;
HardwareInfo.DriveInfo = drive;
HardwareInfo.OsArchitecture = Environment.OSVersion.Platform.ToString() + " " + RuntimeInformation.OSArchitecture.ToString(); // 系统架构
HardwareInfo.FrameworkDescription = RuntimeInformation.FrameworkDescription; // NET框架
HardwareInfo.Environment = App.HostEnvironment.IsDevelopment() ? "Development" : "Production";
HardwareInfo.UUID = HardwareInfo.MachineInfo.UUID;
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
}
}
catch
{
}
try
{
HardwareInfo.MachineInfo.Refresh();
HardwareInfo.UpdateTime = TimerX.Now.ToDefaultDateTimeFormat();
HardwareInfo.WorkingSet = (Environment.WorkingSet / 1024.0 / 1024.0).ToInt();
error = false;
}
catch (Exception ex)
{
if (!error)
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
error = true;
}
try
{
if (HardwareInfoOptions.Enable)
{
if (DateTime.Now > hisInsertTime.Add(TimeSpan.FromMilliseconds(HardwareInfoOptions.HistoryInterval)))
{
hisInsertTime = DateTime.Now;
{
var his = new HistoryHardwareInfo()
{
Date = TimerX.Now,
DriveUsage = (100 - (HardwareInfo.DriveInfo.TotalFreeSpace * 100.00 / HardwareInfo.DriveInfo.TotalSize)).ToInt(),
Battery = (HardwareInfo.MachineInfo.Battery * 100).ToInt(),
MemoryUsage = (HardwareInfo.WorkingSet),
CpuUsage = (HardwareInfo.MachineInfo.CpuRate * 100).ToInt(),
Temperature = (HardwareInfo.MachineInfo.Temperature).ToInt(),
};
await _db.InsertableT(his).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
MemoryCache.Remove(CacheKey);
}
var sevenDaysAgo = TimerX.Now.AddDays(-HardwareInfoOptions.DaysAgo);
//删除特定信息
var result = await _db.Deleteable<HistoryHardwareInfo>(a => a.Date <= sevenDaysAgo).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false);
if (result > 0)
{
MemoryCache.Remove(CacheKey);
}
}
}
error = false;
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
if (!error)
_logger.LogWarning(ex, "Get Hardwareinfo Fail");
error = true;
}
}
}
}

View File

@@ -0,0 +1,41 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
[SugarTable("his_hardwareinfo", TableDescription = "硬件信息历史表")]
[Tenant(SqlSugarConst.DB_HardwareInfo)]
public class HistoryHardwareInfo
{
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "磁盘使用率")]
public int DriveUsage { get; set; }
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "内存")]
public int MemoryUsage { get; set; }
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "CPU使用率")]
public int CpuUsage { get; set; }
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "温度")]
public int Temperature { get; set; }
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "电池")]
public int Battery { get; set; }
/// <inheritdoc/>
[SugarColumn(ColumnDescription = "时间")]
public DateTime Date { get; set; }
}

View File

@@ -0,0 +1,18 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public interface IHardwareJob
{
HardwareInfo HardwareInfo { get; }
HardwareInfoOptions HardwareInfoOptions { get; }
Task<List<HistoryHardwareInfo>> GetHistoryHardwareInfos();
}

View File

@@ -0,0 +1,74 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using ThingsGateway.Schedule;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 作业持久化(数据库)
/// </summary>
public class JobPersistence : IJobPersistence
{
/// <summary>
/// 作业调度服务启动时
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
/// <exception cref="NotSupportedException"></exception>
public async Task<IEnumerable<SchedulerBuilder>> PreloadAsync(CancellationToken stoppingToken)
{
await Task.CompletedTask.ConfigureAwait(false);
// 获取所有定义的作业
var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
return allJobs;
}
/// <summary>
/// 作业计划初始化通知
/// </summary>
/// <param name="builder"></param>
/// <param name="stoppingToken"></param>
/// <returns></returns>
public Task<SchedulerBuilder> OnLoadingAsync(SchedulerBuilder builder, CancellationToken stoppingToken)
{
return Task.FromResult(builder);
}
/// <summary>
/// 作业计划Scheduler的JobDetail变化时
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnChangedAsync(PersistenceContext context)
{
await Task.CompletedTask.ConfigureAwait(false);
}
/// <summary>
/// 作业计划Scheduler的触发器Trigger变化时
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
{
await Task.CompletedTask.ConfigureAwait(false);
}
/// <summary>
/// 作业触发器运行记录
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
{
await Task.CompletedTask.ConfigureAwait(false);
}
}

View File

@@ -0,0 +1,34 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using ThingsGateway.Schedule;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 清理日志作业任务
/// </summary>
[JobDetail("job_log", Description = "清理操作日志", GroupName = "Log", Concurrent = false)]
[Daily(TriggerId = "trigger_log", Description = "清理操作日志", RunOnStart = true)]
public class LogJob : IJob
{
public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
{
var daysAgo = App.GetOptions<AdminLogOptions>()?.OperateLogDaysAgo ?? 7;
await LogJob.DeleteSysOperateLog(daysAgo, stoppingToken).ConfigureAwait(false);
}
private static async Task DeleteSysOperateLog(int daysAgo, CancellationToken stoppingToken)
{
using var db = DbContext.GetDB<SysOperateLog>();
var time = DateTime.Now.AddDays(-daysAgo);
await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.OpTime < time).ExecuteCommandAsync(stoppingToken).ConfigureAwait(false); // 删除操作日志
}
}

View File

@@ -0,0 +1,393 @@
{
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "LoginPolicy",
"PagePolicy": "PagePolicy",
"PasswordPolicy": "PasswordPolicy",
"WebsitePolicy": "WebsitePolicy"
},
"ThingsGateway.Admin.Application.AuthController": {
"AuthController": "Login API",
"LoginAsync": "Login",
"LogoutAsync": "Logout"
},
"ThingsGateway.Admin.Application.AuthService": {
"AuthErrorMax": "Account password error, will be locked for {1} minutes after exceeding {0} times, error count {2}",
"MustDesc": "Password needs to be encrypted with DESC before passing",
"OrgDisable": "The affiliated company/department has been deactivated, please contact the administrator",
"PasswordError": "Too many password errors, please try again in {0} minutes",
"SingleLoginWarn": "Your account is logged in elsewhere",
"TenantNull": "The tenant does not exist",
"UserDisable": "Account {0} has been disabled",
"UserNoModule": "This account has not been assigned a module. Please contact the administrator",
"UserNull": "User {0} does not exist"
},
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
"UserExpire": "User expired, please login again"
},
"ThingsGateway.Admin.Application.DataScopeEnum": {
"SCOPE_ALL": "All",
"SCOPE_ORG": "OnlyOrg",
"SCOPE_ORG_CHILD": "OrgChild",
"SCOPE_ORG_DEFINE": "Define",
"SCOPE_SELF": "Self"
},
"ThingsGateway.Admin.Application.DefaultDataScope": {
"ScopeCategory": "DataScope",
"ScopeDefineOrgIdList": "DefineOrgList"
},
"ThingsGateway.Admin.Application.DictTypeEnum": {
"Define": "Business",
"System": "System"
},
"ThingsGateway.Admin.Application.FileService": {
"FileLengthError": "File size cannot exceed {0} M",
"FileNullError": "File cannot be empty",
"FileTypeError": "Not supported format {0}"
},
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "HostEnvironment",
"FrameworkDescription": ".NETFramework",
"OsArchitecture": "System Architecture",
"UpdateTime": "UpdateTime",
"UUID": "UUID"
},
"ThingsGateway.Admin.Application.HardwareJob": {
"GetHardwareInfoFail": "Get Hardwareinfo Fail"
},
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"Battery": "Battery",
"CpuUsage": "CPU Usage",
"DriveUsage": "Disk Usage",
"MemoryUsage": "Memory",
"Temperature": "Temperature"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Exception": "Exception",
"Login": "Login",
"Logout": "Logout",
"Operate": "Operation"
},
"ThingsGateway.Admin.Application.LogEnum": {
"FAIL": "Fail",
"SUCCESS": "Success"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "Account",
"Account.Required": "{0} is required",
"Password": "Password",
"Password.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"ErrorCount": "Login error count lock threshold",
"ErrorCount.MinValue": "{0} value is too small",
"ErrorLockTime": "Login error lock duration (min)",
"ErrorLockTime.MinValue": "{0} value is too small",
"ErrorResetTime": "Login error count expiration duration (min)",
"ErrorResetTime.MinValue": "{0} value is too small",
"SingleOpen": "Single user login switch",
"VerificatExpireTime": "Login expiration time (min)",
"VerificatExpireTime.MinValue": "{0} value is too small"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.OpenApiAuthController": {
"LoginAsync": "Login",
"LogoutAsync": "Logout",
"OpenApiAuthController": "Login API"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"Account": "Account",
"Category": "Category",
"SearchDate": "SearchDate"
},
"ThingsGateway.Admin.Application.OperDescAttribute": {
"ChangeParentResource": "ChangeParentResource",
"CopyOrg": "Copy Organization",
"CopyResource": "CopyResource",
"DeleteDict": "Delete dictionary",
"DeleteOperLog": "Delete operation log",
"DeleteOrg": "Delete Organization",
"DeletePosition": "Delete Position",
"DeleteResource": "Delete resource",
"DeleteRole": "Delete role",
"DeleteuSER": "Delete user",
"EditLoginPolicy": "Modify login policy",
"EditPagePolicy": "Modify page policy",
"EditPasswordPolicy": "Modify password policy",
"EditWebsitePolicy": "Modify website settings",
"ExitSession": "Force session off",
"ExitVerificat": "Force token off",
"ExportOperLog": "Export operation log",
"GrantApi": "GrantApi",
"GrantResource": "GrantResource",
"GrantRole": "GrantRole",
"GrantUser": "GrantUser",
"NoPermission": "No Permission",
"ResetPassword": "Reset pw",
"RoleGrantApiPermission": "Role grant OpenApi",
"RoleGrantResource": "Role grant resource",
"RoleGrantUser": "Role grant user",
"SaveDict": "Modify dictionary",
"SaveOrg": "Save Organization",
"SavePosition": "Save Position",
"SaveResource": "Modify resource",
"SaveRole": "Modify role",
"SaveUser": "Modify user",
"UpdatePassword": "Update personal password",
"UpdateUserInfo": "Update personal information",
"UserGrantApiPermission": "User grant OpenApi",
"UserGrantResource": "User grant resource",
"UserGrantRole": "User grant role",
"WorkbenchInfo": "Update personal workbench"
},
"ThingsGateway.Admin.Application.OrgEnum": {
"COMPANY": "Company",
"DEPT": "Dept"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Razor": "Default homepage",
"Shortcuts": "Default shortcuts"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "Default user password",
"DefaultPassword.Required": "{0} is required",
"PasswordContainChar": "Contain special characters",
"PasswordContainLower": "Contain lowercase letters",
"PasswordContainNum": "Contain numbers",
"PasswordContainUpper": "Contain uppercase letters",
"PasswordMinLen": "Minimum password length",
"PasswordMinLen.MinValue": "{0} value is too small"
},
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
"HIGH": "High",
"LOW": "Low",
"MIDDLE": "Middle"
},
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"Button": "Button",
"Menu": "Menu",
"Module": "Module"
},
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
"Href": "Path",
"Module": "Module",
"Title": "Title"
},
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "Global",
"Org": "Org"
},
"ThingsGateway.Admin.Application.SessionOutput": {
"Account": "Account",
"LatestLoginIp": "Latest login IP",
"LatestLoginTime": "Latest login time",
"Online": "Online status",
"VerificatCount": "Token count"
},
"ThingsGateway.Admin.Application.SysDict": {
"Category": "Category",
"Category.Required": "{0} is required",
"Code": "Code",
"Code.Required": "{0} is required",
"DemoCanotUpdateWebsitePolicy": "DEMO environment does not allow modifying website settings",
"DictDup": "Duplicate configuration exists, category {0}, name {1}",
"Name": "Name",
"Name.Required": "{0} is required",
"Remark": "Remark"
},
"ThingsGateway.Admin.Application.SysOperateLog": {
"Category": "Category",
"ClassName": "ClassName",
"ExeMessage": "ExeMessage",
"ExeStatus": "ExeStatus",
"MethodName": "MethodName",
"Name": "Name",
"OpAccount": "OpAccount",
"OpBrowser": "OpBrowser",
"OpIp": "OpIp",
"OpOs": "OpOs",
"OpTime": "OpTime",
"ParamJson": "ParamJson",
"ReqMethod": "RequestMethod",
"ReqUrl": "RequestUrl",
"ResultJson": "ResultJson",
"VerificatId": "VerificatId"
},
"ThingsGateway.Admin.Application.SysOrg": {
"CanotContainsSelf": "Cannot contain itself",
"Category": "Category",
"Category.Required": "{0} is a required field",
"Code": "Code",
"Code.Required": "{0} is a required field",
"CodeDup": "Duplicate code {0} exists",
"DeletePositionFirst": "Please remove the positions under the organization first",
"DeleteRoleFirst": "Please remove the roles under the organization first",
"DeleteUserFirst": "Please remove the users under the organization first",
"DirectorId": "Director",
"Dup": "Duplicate organization exists with Category {0} and Name {1}",
"Name": "Name",
"Name.Required": "{0} is a required field",
"NameDup": "Duplicate name {0} exists",
"Names": "Names",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentId": "ParentOrg",
"ParentNull": "Parent does not exist {0}",
"Remark": "Remarks",
"RootOrg": "Unable to create top-level organization",
"Status": "Status",
"TargetNameDup": "Target node has duplicate name {0}"
},
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"ContainsChild": "ContainsChild",
"ContainsPosition": "ContainsPosition",
"TargetId": "Target"
},
"ThingsGateway.Admin.Application.SysPosition": {
"CanotContainsSelf": "Cannot contain itself",
"Category": "Category",
"Category.Required": "{0} is a required field",
"Code": "Code",
"Code.Required": "{0} is a required field",
"CodeDup": "Duplicate code {0} exists",
"DeleteUserFirst": "Please remove the users under the position first",
"Dup": "Duplicate position exists with Category {0} and Name {1}",
"Name": "Name",
"Name.Required": "{0} is a required field",
"NameDup": "Duplicate name {0} exists",
"OrgId": "Organization",
"OrgId.MinValue": "{0} is a required field",
"ParentChoiceSelf": "Parent cannot be itself",
"ParentNull": "Parent does not exist {0}",
"Remark": "Remarks",
"Status": "Status",
"TargetNameDup": "Target node has duplicate name {0}"
},
"ThingsGateway.Admin.Application.SysResource": {
"CanotDeleteSystemResource": "Cannot delete system resource {0}",
"Category": "Category",
"Code": "Code",
"Href": "Path",
"Href.Required": "{0} is required",
"Icon": "Icon",
"Module": "Module",
"ModuleIdDiff": "Module is inconsistent with parent menu",
"NavLinkMatch": "NavLinkMatch",
"NotFoundResource": "System exception, menu not found",
"ParentId": "Parent",
"ResourceDup": "Duplicate name {0} exists",
"ResourceMenuHrefNotNull": "Menu href cannot null",
"ResourceParentChoiceSelf": "Parent cannot choose itself",
"ResourceParentNull": "Parent does not exist {0}",
"Target": "Target",
"Title": "Title",
"Title.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.SysRole": {
"CannotRoleScopeAll": "Organization role cannot select global data scope",
"CanotDeleteAdmin": "Cannot delete built-in super admin role",
"CanotEditAdmin": "Cannot edit super admin role",
"CanotGrantAdmin": "Cannot assign admins roles",
"Category": "Category",
"Code": "Code",
"CodeDup": "Duplicate code exists: {0}",
"Global": "Global",
"Name": "Name",
"Name.Required": "{0} is required",
"NameDup": "Duplicate role name {0}",
"OrgId": "Org",
"OrgNotNull": "Organization cannot be null",
"SameOrgNameDup": "Duplicate role name exists: {0}",
"Status": "Status"
},
"ThingsGateway.Admin.Application.SysUser": {
"Account": "Account",
"Account.Required": "Account.Required",
"AccountDup": "Duplicate account {0} exists",
"Avatar": "Avatar",
"CanotDeleteAdminUser": "Cannot delete built-in super admin user",
"CanotDeleteSelf": "Cannot delete yourself",
"CanotEditAdminUser": "Cannot edit super admin user",
"CanotGrantAdmin": "Cannot assign admins roles",
"CheckSelf": "Prohibit {0} yourself",
"ConfirmPasswordDiff": "Passwords entered twice are inconsistent",
"DemoCanotUpdatePassword": "DEMO environment does not allow password modification",
"DirectorId": "Director",
"DirectorSelf": "Cannot set oneself as the supervisor",
"Disable": "Disable",
"Email": "Email",
"EmailDup": "Duplicate email {0} exists",
"EmailError": "Email format error {0}",
"Enable": "Enable",
"ExitVerificat": "You have been forcibly logged out",
"GrantRole": "GrantRole",
"LastLoginAddress": "LastLoginAddress",
"LastLoginDevice": "LastLoginDevice",
"LastLoginIp": "LastLoginIp",
"LastLoginTime": "LastLoginTime",
"LatestLoginAddress": "LatestLoginAddress",
"LatestLoginDevice": "LatestLoginDevice",
"LatestLoginIp": "LatestLoginIp",
"LatestLoginTime": "LatestLoginTime",
"NoOrg": "The organization does not exist",
"OldPasswordError": "Incorrect old password",
"OrgId": "Org",
"OrgNames": "OrgNames",
"Password": "Password",
"PasswordEdited": "Password changed, logged out",
"PasswordLengthLess": "Password length cannot be less than {0}",
"PasswordMustLow": "Password must contain lowercase letters",
"PasswordMustNum": "Password must contain numbers",
"PasswordMustSpecial": "Password must contain special characters",
"PasswordMustUpp": "Password must contain uppercase letters",
"Phone": "Phone",
"PhoneError": "Phone number format error {0}",
"PositionId": "Position",
"PositionName": "PositionName",
"Status": "Status"
},
"ThingsGateway.Admin.Application.TargetEnum": {
"_blank": "New window",
"_parent": "Parent window",
"_self": "Current window",
"_top": "Top window"
},
"ThingsGateway.Admin.Application.TestController": {
"Test": "Test",
"TestController": "Test API"
},
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"NoPermission": "Access denied, no permission",
"TokenOver": "Login has expired, please login again"
},
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"ConfirmPassword": "Confirm password",
"ConfirmPassword.Required": "{0} is required",
"NewPassword": "New password",
"NewPassword.Required": "{0} is required",
"Password": "Password",
"Password.Required": "{0} is required"
},
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"Account": "Account",
"OrgId": "Org"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Device": "Device",
"Expire": "Expire(min)",
"LoginIp": "LoginIp",
"LoginTime": "LoginTime",
"Online": "Online",
"VerificatTimeout": "VerificatTimeout"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"CloseTip": "CloseTip",
"CloseTip.Required": "{0} is required",
"WebStatus": "WebStatus"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "Homepage",
"Shortcuts": "Shortcuts"
}
}

View File

@@ -0,0 +1,393 @@
{
"ThingsGateway.Admin.Application.AppConfig": {
"LoginPolicy": "登录策略",
"PagePolicy": "页面设置",
"PasswordPolicy": "密码策略",
"WebsitePolicy": "网站设置"
},
"ThingsGateway.Admin.Application.AuthController": {
"AuthController": "登录API",
"LoginAsync": "登录",
"LogoutAsync": "注销"
},
"ThingsGateway.Admin.Application.AuthService": {
"AuthErrorMax": "账号密码错误,超过 {0} 次后将锁定 {1} 分钟,错误次数 {2} ",
"MustDesc": "密码需要DESC加密后传入",
"OrgDisable": "所属公司/部门已停用,请联系管理员",
"PasswordError": "密码错误次数过多,请 {0} 分钟后再试",
"SingleLoginWarn": "您的账号已在别处登录",
"TenantNull": "租户不存在",
"UserDisable": "账号 {0} 已停用",
"UserNoModule": "该账号未分配模块,请联系管理员",
"UserNull": "用户 {0} 不存在"
},
"ThingsGateway.Admin.Application.BlazorAuthenticationHandler": {
"UserExpire": "用户登录已过期,请重新登录"
},
"ThingsGateway.Admin.Application.DataScopeEnum": {
"SCOPE_ALL": "全部",
"SCOPE_ORG": "仅所属组织",
"SCOPE_ORG_CHILD": "所属组织及以下",
"SCOPE_ORG_DEFINE": "自定义",
"SCOPE_SELF": "仅自己"
},
"ThingsGateway.Admin.Application.DefaultDataScope": {
"ScopeCategory": "数据范围",
"ScopeDefineOrgIdList": "自定义列表"
},
"ThingsGateway.Admin.Application.DictTypeEnum": {
"Define": "业务配置",
"System": "系统配置"
},
"ThingsGateway.Admin.Application.FileService": {
"FileLengthError": "文件大小不允许超过 {0} M",
"FileNullError": "文件不能为空",
"FileTypeError": "不支持 {0} 格式"
},
"ThingsGateway.Admin.Application.HardwareInfo": {
"Environment": "主机环境",
"FrameworkDescription": "NET框架",
"OsArchitecture": "系统架构",
"UpdateTime": "更新时间",
"UUID": "唯一编码"
},
"ThingsGateway.Admin.Application.HardwareJob": {
"GetHardwareInfoFail": "获取硬件信息出错"
},
"ThingsGateway.Admin.Application.HistoryHardwareInfo": {
"Battery": "电池",
"CpuUsage": "CPU使用率",
"DriveUsage": "磁盘使用率",
"MemoryUsage": "内存",
"Temperature": "温度"
},
"ThingsGateway.Admin.Application.LogCateGoryEnum": {
"Exception": "异常",
"Login": "登录",
"Logout": "注销",
"Operate": "操作"
},
"ThingsGateway.Admin.Application.LogEnum": {
"FAIL": "失败",
"SUCCESS": "成功"
},
"ThingsGateway.Admin.Application.LoginInput": {
"Account": "登录账号",
"Account.Required": "{0} 是必填项",
"Password": "登录密码",
"Password.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.LoginPolicy": {
"ErrorCount": "登录错误次数锁定阈值",
"ErrorCount.MinValue": " {0} 值太小",
"ErrorLockTime": "登录错误锁定时长(分)",
"ErrorLockTime.MinValue": " {0} 值太小",
"ErrorResetTime": "登录错误次数过期时长(分)",
"ErrorResetTime.MinValue": " {0} 值太小",
"SingleOpen": "单用户登录开关",
"VerificatExpireTime": "登录过期时间(分)",
"VerificatExpireTime.MinValue": " {0} 值太小"
},
"ThingsGateway.Admin.Application.LogoutInput": {
"VerificatId.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.OpenApiAuthController": {
"LoginAsync": "登录",
"LogoutAsync": "注销",
"OpenApiAuthController": "登录API"
},
"ThingsGateway.Admin.Application.OperateLogPageInput": {
"Account": "操作账号",
"Category": "分类",
"SearchDate": "时间范围"
},
"ThingsGateway.Admin.Application.OperDescAttribute": {
"ChangeParentResource": "更改父节点",
"CopyOrg": "复制机构",
"CopyResource": "复制资源",
"DeleteDict": "删除字典",
"DeleteOperLog": "删除操作日志",
"DeleteOrg": "删除机构",
"DeletePosition": "删除岗位",
"DeleteResource": "删除资源",
"DeleteRole": "删除角色",
"DeleteuSER": "删除用户",
"EditLoginPolicy": "修改登录策略",
"EditPagePolicy": "修改页面策略",
"EditPasswordPolicy": "修改密码策略",
"EditWebsitePolicy": "修改网站设置",
"ExitSession": "强退会话",
"ExitVerificat": "强退令牌",
"ExportOperLog": "导出操作日志",
"GrantApi": "API",
"GrantResource": "资源",
"GrantRole": "角色",
"GrantUser": "用户",
"NoPermission": "无权限操作",
"ResetPassword": "重置密码",
"RoleGrantApiPermission": "角色授权OpenApi",
"RoleGrantResource": "角色授权资源",
"RoleGrantUser": "角色授权用户",
"SaveDict": "修改字典",
"SaveOrg": "保存机构",
"SavePosition": "保存岗位",
"SaveResource": "修改资源",
"SaveRole": "修改角色",
"SaveUser": "修改用户",
"UpdatePassword": "更新个人密码",
"UpdateUserInfo": "更新个人信息",
"UserGrantApiPermission": "用户授权OpenApi",
"UserGrantResource": "用户授权资源",
"UserGrantRole": "用户授权角色",
"WorkbenchInfo": "更新个人工作台"
},
"ThingsGateway.Admin.Application.OrgEnum": {
"COMPANY": "公司",
"DEPT": "部门"
},
"ThingsGateway.Admin.Application.PagePolicy": {
"Razor": "默认主页",
"Shortcuts": "默认快捷方式"
},
"ThingsGateway.Admin.Application.PasswordPolicy": {
"DefaultPassword": "默认用户密码",
"DefaultPassword.Required": " {0} 是必填项",
"PasswordContainChar": "包含特殊字符",
"PasswordContainLower": "包含小写字母",
"PasswordContainNum": "包含数字",
"PasswordContainUpper": "包含大写字母",
"PasswordMinLen": "密码最小长度",
"PasswordMinLen.MinValue": " {0} 值太小"
},
"ThingsGateway.Admin.Application.PositionCategoryEnum": {
"HIGH": "高层",
"LOW": "低层",
"MIDDLE": "中层"
},
"ThingsGateway.Admin.Application.ResourceCategoryEnum": {
"Button": "按钮",
"Menu": "菜单",
"Module": "模块"
},
"ThingsGateway.Admin.Application.ResourceTableSearchModel": {
"Href": "路径",
"Module": "模块",
"Title": "标题"
},
"ThingsGateway.Admin.Application.RoleCategoryEnum": {
"Global": "全局",
"Org": "机构"
},
"ThingsGateway.Admin.Application.SessionOutput": {
"Account": "账号",
"LatestLoginIp": "最新登录ip",
"LatestLoginTime": "最新登录时间",
"Online": "在线状态",
"VerificatCount": "令牌数量"
},
"ThingsGateway.Admin.Application.SysDict": {
"Category": "分类",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Code.Required": "{0} 是必填项",
"DemoCanotUpdateWebsitePolicy": "DEMO环境不允许修改网站设置",
"DictDup": "存在重复的配置 分类 {0} 名称 {1}",
"Name": "名称",
"Name.Required": "{0} 是必填项",
"Remark": "备注"
},
"ThingsGateway.Admin.Application.SysOperateLog": {
"Category": "日志分类",
"ClassName": "类名",
"ExeMessage": "具体消息",
"ExeStatus": "执行状态",
"MethodName": "方法名称",
"Name": "日志名称",
"OpAccount": "账号",
"OpBrowser": "浏览器",
"OpIp": "ip",
"OpOs": "系统",
"OpTime": "操作时间",
"ParamJson": "请求参数",
"ReqMethod": "请求方式",
"ReqUrl": "请求地址",
"ResultJson": "返回结果",
"VerificatId": "验证Id"
},
"ThingsGateway.Admin.Application.SysOrg": {
"CanotContainsSelf": "不可包含自己",
"Category": "分类",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Code.Required": "{0} 是必填项",
"CodeDup": "存在重复的编码 {0}",
"DeletePositionFirst": "请先删除机构下的职位",
"DeleteRoleFirst": "请先删除机构下的角色",
"DeleteUserFirst": "请先删除机构下的用户",
"DirectorId": "主管",
"Dup": "存在重复的机构 分类 {0} 名称 {1}",
"Name": "名称",
"Name.Required": "{0} 是必填项",
"NameDup": "存在重复的名称 {0}",
"Names": "机构全称",
"ParentChoiceSelf": "父级不能选择自己",
"ParentId": "上级机构",
"ParentNull": "父级不存在 {0}",
"Remark": "备注",
"RootOrg": "无法创建顶层机构",
"Status": "状态",
"TargetNameDup": "目标节点存在重复的名称 {0}"
},
"ThingsGateway.Admin.Application.SysOrgCopyInput": {
"ContainsChild": "包含下级",
"ContainsPosition": "包含职位",
"TargetId": "目标机构"
},
"ThingsGateway.Admin.Application.SysPosition": {
"CanotContainsSelf": "不可包含自己",
"Category": "分类",
"Category.Required": "{0} 是必填项",
"Code": "代码",
"Code.Required": "{0} 是必填项",
"CodeDup": "存在重复的编码 {0}",
"DeleteUserFirst": "请先删除职位下的用户",
"Dup": "存在重复的岗位 分类 {0} 名称 {1}",
"Name": "名称",
"Name.Required": "{0} 是必填项",
"NameDup": "存在重复的名称 {0}",
"OrgId": "机构",
"OrgId.MinValue": "{0} 是必填项",
"ParentChoiceSelf": "父级不能选择自己",
"ParentNull": "父级不存在 {0}",
"Remark": "备注",
"Status": "状态",
"TargetNameDup": "目标节点存在重复的名称 {0}"
},
"ThingsGateway.Admin.Application.SysResource": {
"CanotDeleteSystemResource": "不可删除系统资源 {0}",
"Category": "分类",
"Code": "编码",
"Href": "路径",
"Href.Required": "{0} 是必填项",
"Icon": "图标",
"Module": "模块",
"ModuleIdDiff": "模块与上级菜单不一致",
"NavLinkMatch": "匹配类型",
"NotFoundResource": "系统异常,没找到该菜单",
"ParentId": "上级菜单",
"ResourceDup": "存在重复的名称 {0}",
"ResourceMenuHrefNotNull": "菜单的路径不能为空",
"ResourceParentChoiceSelf": "父级不能选择自己",
"ResourceParentNull": "父级不存在 {0}",
"Target": "跳转类型",
"Title": "标题",
"Title.Required": "{0} 是必填项"
},
"ThingsGateway.Admin.Application.SysRole": {
"CannotRoleScopeAll": "机构角色不能选择全局数据范围",
"CanotDeleteAdmin": "不可删除系统内置超管角色",
"CanotEditAdmin": "不可编辑超管角色",
"CanotGrantAdmin": "不能分配超管角色",
"Category": "分类",
"Code": "编码",
"CodeDup": "存在重复的编码 {0}",
"Global": "全局",
"Name": "名称",
"Name.Required": " {0} 是必填项",
"NameDup": "存在重复的角色名称 {0}",
"OrgId": "机构",
"OrgNotNull": "机构不能为空",
"SameOrgNameDup": "存在重复的角色名称 {0}",
"Status": "状态"
},
"ThingsGateway.Admin.Application.SysUser": {
"Account": "账号",
"Account.Required": " {0} 是必填项",
"AccountDup": "存在重复的账号 {0}",
"Avatar": "头像",
"CanotDeleteAdminUser": "不可删除系统内置超管用户",
"CanotDeleteSelf": "不可删除自己",
"CanotEditAdminUser": "不可编辑超管用户",
"CanotGrantAdmin": "不能分配超管角色",
"CheckSelf": "禁止 {0} 自己",
"ConfirmPasswordDiff": "两次输入的密码不一致",
"DemoCanotUpdatePassword": "DEMO环境不允许修改密码",
"DirectorId": "主管",
"DirectorSelf": "不能设置自己为主管",
"Disable": "禁用",
"Email": "邮箱",
"EmailDup": "存在重复的邮箱 {0}",
"EmailError": "邮箱 {0} 格式错误",
"Enable": "启用",
"ExitVerificat": "您已被强制下线",
"GrantRole": "分配角色",
"LastLoginAddress": "上次登录地点",
"LastLoginDevice": "上次登录设备",
"LastLoginIp": "上次登录ip",
"LastLoginTime": "上次登录时间",
"LatestLoginAddress": "最新登录地点",
"LatestLoginDevice": "最新登录设备",
"LatestLoginIp": "最新登录ip",
"LatestLoginTime": "最新登录时间",
"NoOrg": "组织机构不存在",
"OldPasswordError": "原密码错误",
"OrgId": "机构",
"OrgNames": "机构名称",
"Password": "密码",
"PasswordEdited": "密码被修改,已退出登录",
"PasswordLengthLess": "密码长度不能小于 {0} ",
"PasswordMustLow": "密码必须包含小写字母",
"PasswordMustNum": "密码必须包含数字",
"PasswordMustSpecial": "密码必须包含特殊字符",
"PasswordMustUpp": "密码必须包含大写字母",
"Phone": "手机",
"PhoneError": "手机号码 {0} 格式错误",
"PositionId": "职位",
"PositionName": "职位名称",
"Status": "状态"
},
"ThingsGateway.Admin.Application.TargetEnum": {
"_blank": "新窗口",
"_parent": "父级窗口",
"_self": "本窗口",
"_top": "顶级窗口"
},
"ThingsGateway.Admin.Application.TestController": {
"Test": "测试",
"TestController": "测试API"
},
"ThingsGateway.Admin.Application.UnifyResultProvider": {
"NoPermission": "禁止访问,没有权限",
"TokenOver": "登录已过期,请重新登录"
},
"ThingsGateway.Admin.Application.UpdatePasswordInput": {
"ConfirmPassword": "确认密码",
"ConfirmPassword.Required": " {0} 是必填项",
"NewPassword": "新密码",
"NewPassword.Required": " {0} 是必填项",
"Password": "密码",
"Password.Required": " {0} 是必填项"
},
"ThingsGateway.Admin.Application.UserSelectorOutput": {
"Account": "账号",
"OrgId": "机构"
},
"ThingsGateway.Admin.Application.VerificatInfo": {
"Device": "登录设备",
"Expire": "过期时间(分)",
"LoginIp": "登录IP",
"LoginTime": "登录时间",
"Online": "在线状态",
"VerificatTimeout": "超时时间"
},
"ThingsGateway.Admin.Application.WebsitePolicy": {
"CloseTip": "关闭提示",
"CloseTip.Required": " {0} 是必填项",
"WebStatus": "是否开放"
},
"ThingsGateway.Admin.Application.WorkbenchInfo": {
"Razor": "主页",
"Shortcuts": "快捷方式"
}
}

View File

@@ -0,0 +1,208 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.Collections.Concurrent;
using System.Reflection;
using ThingsGateway.Extension;
using ThingsGateway.FriendlyException;
using ThingsGateway.Logging;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 数据库写入器
/// </summary>
public class DatabaseLoggingWriter : IDatabaseLoggingWriter
{
/// <summary>
/// 日志消息队列(线程安全)
/// </summary>
private readonly ConcurrentQueue<SysOperateLog> _operateLogMessageQueue = new();
private SqlSugarClient SqlSugarClient;
/// <summary>
/// 此方法只会写入经由MVCFilter捕捉的方法日志对于BlazorServer的内部操作由<see cref="OperDescAttribute"/>执行
/// </summary>
/// <param name="logMsg"></param>
/// <param name="flush"></param>
public async Task WriteAsync(LogMessage logMsg, bool flush)
{
//转成实体
var requestAuditData = logMsg.Context.Get(nameof(RequestAuditData)) as RequestAuditData;
//日志时间赋值
requestAuditData.LogDateTime = logMsg.LogDateTime;
// requestAuditData.ReturnInformation.Value
//验证失败不记录日志
bool save = false;
if (requestAuditData.Validation == null)
{
var operation = requestAuditData.Operation;//获取操作名称
var client = requestAuditData.Client;//获取客户端信息
var path = requestAuditData.Path;//获取操作名称
var method = requestAuditData.Method;//获取方法
var methodInfo = requestAuditData.MethodInfo;
var login = methodInfo.GetCustomAttribute(typeof(LoginLogAttribute));
var logout = methodInfo.GetCustomAttribute(typeof(LogoutLogAttribute));
//表示访问日志
if (login != null || logout != null)
{
//如果没有异常信息
if (requestAuditData.Exception == null)
{
LogCateGoryEnum logCateGoryEnum = login != null ? LogCateGoryEnum.Login : LogCateGoryEnum.Logout;
save = await CreateVisitLog(operation, path, requestAuditData, client, logCateGoryEnum, flush).ConfigureAwait(false);//添加到访问日志
}
else
{
//添加到异常日志
save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);
}
}
else
{
//只有定义了Title的POST方法才记录日志
if (!operation.IsNullOrWhiteSpace() && method == "POST")
{
//添加到操作日志
save = await CreateOperationLog(operation, path, requestAuditData, client, flush).ConfigureAwait(false);
}
}
}
if (save)
{
await Task.Delay(1000).ConfigureAwait(false);
}
}
/// <summary>
/// 创建操作日志
/// </summary>
/// <param name="operation">操作名称</param>
/// <param name="path">请求地址</param>
/// <param name="requestAuditData">requestAuditData</param>
/// <param name="userAgent">客户端信息</param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task<bool> CreateOperationLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, bool flush)
{
//账号
var opAccount = requestAuditData.AuthorizationClaims?.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
//获取参数json字符串
var paramJson = requestAuditData.Parameters == null || requestAuditData.Parameters.Count == 0 ? null : requestAuditData.Parameters.ToSystemTextJsonString();
//获取结果json字符串
var resultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString();
//操作日志表实体
var sysLogOperate = new SysOperateLog
{
Name = operation,
Category = LogCateGoryEnum.Operate,
ExeStatus = true,
OpIp = requestAuditData.RemoteIPv4,
OpBrowser = userAgent?.Browser,
OpOs = userAgent?.Platform,
OpTime = requestAuditData.LogDateTime.LocalDateTime,
OpAccount = opAccount,
ReqMethod = requestAuditData.Method,
ReqUrl = path,
ResultJson = resultJson,
ClassName = requestAuditData.ControllerName,
MethodName = requestAuditData.ActionName,
ParamJson = paramJson,
VerificatId = UserManager.VerificatId,
};
//如果异常不为空
if (requestAuditData.Exception != null)
{
sysLogOperate.Category = LogCateGoryEnum.Exception;//操作类型为异常
sysLogOperate.ExeStatus = false;//操作状态为失败
if (requestAuditData.Exception.Type == typeof(AppFriendlyException).ToString())
sysLogOperate.ExeMessage = requestAuditData?.Exception.Message;
else
sysLogOperate.ExeMessage = $"{requestAuditData.Exception.Type}:{requestAuditData.Exception.Message}{Environment.NewLine}{requestAuditData.Exception.StackTrace}";
}
_operateLogMessageQueue.Enqueue(sysLogOperate);
if (flush)
{
SqlSugarClient ??= DbContext.GetDB<SysOperateLog>();
await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库
return true;
}
return false;
}
/// <summary>
/// 创建访问日志
/// </summary>
/// <param name="operation">访问类型</param>
/// <param name="path"></param>
/// <param name="requestAuditData">requestAuditData</param>
/// <param name="userAgent">客户端信息</param>
/// <param name="logCateGoryEnum">logCateGory</param>
/// <param name="flush"></param>
private async Task<bool> CreateVisitLog(string operation, string path, RequestAuditData requestAuditData, UserAgent userAgent, LogCateGoryEnum logCateGoryEnum, bool flush)
{
long verificatId = 0;//验证Id
var opAccount = "";//用户账号
if (logCateGoryEnum == LogCateGoryEnum.Login)
{
//如果是登录,用户信息就从返回值里拿
if (requestAuditData.ReturnInformation is UnifyResult<LoginOutput> userInfo)
{
opAccount = userInfo.Data.Account;//赋值账号
verificatId = userInfo.Data.VerificatId;
}
}
else
{
//如果是登录出用户信息就从AuthorizationClaims里拿
opAccount = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.Account).Select(it => it.Value).FirstOrDefault();
verificatId = requestAuditData.AuthorizationClaims.Where(it => it.Type == ClaimConst.VerificatId).Select(it => it.Value).FirstOrDefault().ToLong();
}
//日志表实体
var sysLogVisit = new SysOperateLog
{
Name = operation,
Category = logCateGoryEnum,
ExeStatus = true,
OpIp = requestAuditData.RemoteIPv4,
OpBrowser = userAgent?.Browser,
OpOs = userAgent?.Platform,
OpTime = requestAuditData.LogDateTime.LocalDateTime,
VerificatId = verificatId,
OpAccount = opAccount,
ReqMethod = requestAuditData.Method,
ReqUrl = path,
ResultJson = requestAuditData.ReturnInformation?.ToSystemTextJsonString(),
ClassName = requestAuditData.ControllerName,
MethodName = requestAuditData.ActionName,
ParamJson = requestAuditData.Parameters?.ToSystemTextJsonString(),
};
_operateLogMessageQueue.Enqueue(sysLogVisit);
if (flush)
{
SqlSugarClient ??= DbContext.GetDB<SysOperateLog>();
await SqlSugarClient.InsertableWithAttr(_operateLogMessageQueue.ToListWithDequeue()).ExecuteCommandAsync().ConfigureAwait(false);//入库
return true;
}
return false;
}
}

View File

@@ -0,0 +1,42 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 日志常量
/// </summary>
public static class LoggingConst
{
/// <summary>
/// 分类
/// </summary>
public const string CateGory = "CateGory";
/// <summary>
/// 客户端信息
/// </summary>
public const string Client = "Client";
/// <summary>
/// 请求方法POST/GET
/// </summary>
public const string Method = "Method";
/// <summary>
/// 操作名称
/// </summary>
public const string Operation = "Operation";
/// <summary>
/// 请求地址
/// </summary>
public const string Path = "Path";
}

View File

@@ -0,0 +1,187 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 认证信息
/// </summary>
public class AuthorizationClaims
{
/// <summary>
/// 类型
/// </summary>
public string Type { get; set; }
/// <summary>
/// 值
/// </summary>
public string Value { get; set; }
}
/// <summary>
/// 异常信息
/// </summary>
public class LogException
{
/// <summary>
/// 异常内容
/// </summary>
public string Message { get; set; }
/// <summary>
/// 堆栈信息
/// </summary>
public string StackTrace { get; set; }
/// <summary>
/// 异常类型
/// </summary>
public string Type { get; set; }
}
/// <summary>
/// 请求信息格式化
/// </summary>
public class LoggingMonitorJson
{
/// <summary>
/// 方法名称
/// </summary>
public string ActionName { get; set; }
/// <summary>
/// 认证信息
/// </summary>
public List<AuthorizationClaims> AuthorizationClaims { get; set; }
/// <summary>
/// 控制器名
/// </summary>
public string ControllerName { get; set; }
/// <summary>
/// 类名称
/// </summary>
public string DisplayName { get; set; }
/// <summary>
/// 环境
/// </summary>
public string Environment { get; set; }
/// <summary>
/// 异常信息
/// </summary>
public LogException Exception { get; set; }
/// <summary>
/// 请求方法
/// </summary>
public string HttpMethod { get; set; }
/// <summary>
/// 服务端
/// </summary>
public string LocalIPv4 { get; set; }
/// <summary>
/// 日志时间
/// </summary>
public DateTimeOffset LogDateTime { get; set; }
/// <summary>
/// 系统架构
/// </summary>
public string OsArchitecture { get; set; }
/// <summary>
/// 系统名称
/// </summary>
public string OsDescription { get; set; }
/// <summary>
/// 参数列表
/// </summary>
public List<Parameters> Parameters { get; set; }
/// <summary>
/// 客户端IPV4地址
/// </summary>
public string RemoteIPv4 { get; set; }
/// <summary>
/// 认证信息
/// </summary>
public string RequestHeaderAuthorization { get; set; }
/// <summary>
/// 认证信息
/// </summary>
public string RequestHeaderCookies { get; set; }
/// <summary>
/// 请求地址
/// </summary>
public string RequestUrl { get; set; }
/// <summary>
/// 返回信息
/// </summary>
public ReturnInformation ReturnInformation { get; set; }
/// <summary>
/// 浏览器标识
/// </summary>
public string UserAgent { get; set; }
/// <summary>
/// 验证错误信息
/// </summary>
public Validation Validation { get; set; }
}
/// <summary>
/// 请求参数
/// </summary>
public class Parameters
{
/// <summary>
/// 参数名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 值
/// </summary>
public object Value { get; set; }
}
/// <summary>
/// 返回信息
/// </summary>
public class ReturnInformation
{
/// <summary>
/// 返回值
/// </summary>
public object Value { get; set; }
}
/// <summary>
/// 验证失败信息
/// </summary>
public class Validation
{
/// <summary>
/// 错误详情
/// </summary>
public string Message { get; set; }
}

View File

@@ -0,0 +1,28 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Riok.Mapperly.Abstractions;
namespace ThingsGateway.Admin.Application;
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
public static partial class AdminMapper
{
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);
public static partial SessionOutput AdaptSessionOutput(this SysUser src);
public static partial SysUser AdaptSysUser(this SysUser src);
public static partial UserSelectorOutput AdaptUserSelectorOutput(this SysUser src);
public static partial List<SysResource> AdaptListSysResource(this IEnumerable<SysResource> src);
public static partial AppConfig AdaptAppConfig(this AppConfig src);
public static partial WorkbenchInfo AdaptWorkbenchInfo(this WorkbenchInfo src);
public static partial QueryData<UserSelectorOutput> AdaptQueryDataUserSelectorOutput(this QueryData<SysUser> src);
public static partial LoginInput AdaptLoginInput(this LoginInput src);
}

View File

@@ -0,0 +1,32 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
public class USheetDatas
{
public string locale { get; set; } = System.Globalization.CultureInfo.CurrentUICulture.Name;
public Dictionary<string, USheetData> sheets { get; set; } = new();
}
public class USheetData
{
public string id { get; set; }
public string name { get; set; }
public int rowCount { get; set; }
public int columnCount { get; set; }
public Dictionary<int, Dictionary<int, USheetCelldata>> cellData { get; set; } = new();
}
public class USheetCelldata
{
public object v { get; set; }
}

View File

@@ -0,0 +1,228 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System.Security.Claims;
using System.Text.Encodings.Web;
using ThingsGateway.Extension;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 只适合 Demo 登录,会直接授权超管的权限
/// </summary>
public class AdminOAuthHandler<TOptions>(
IVerificatInfoService verificatInfoService,
IAppService appService,
ISysUserService sysUserService,
ISysDictService configService,
IOptionsMonitor<TOptions> options,
ILoggerFactory logger,
IUserAgentService userAgentService,
UrlEncoder encoder
) : OAuthHandler<TOptions>(options, logger, encoder)
where TOptions : AdminOAuthOptions, new()
{
static AdminOAuthHandler()
{
Task.Factory.StartNew(InsertableAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
}
/// <summary>
/// 日志消息队列(线程安全)
/// </summary>
protected static readonly ConcurrentQueue<SysOperateLog> _operateLogMessageQueue = new();
/// <summary>
/// 创建访问日志
/// </summary>
private static async Task InsertableAsync()
{
var db = DbContext.GetDB<SysOperateLog>();
var appLifetime = App.RootServices!.GetService<IHostApplicationLifetime>()!;
while (!appLifetime.ApplicationStopping.IsCancellationRequested)
{
try
{
var data = _operateLogMessageQueue.ToListWithDequeue(); // 从日志队列中获取数据
if (data.Count > 0)
{
await db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);//入库
}
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
await Task.Delay(3000, appLifetime.ApplicationStopping).ConfigureAwait(false);
}
}
}
protected override async Task<AuthenticationTicket> CreateTicketAsync(
ClaimsIdentity identity,
AuthenticationProperties properties,
OAuthTokenResponse tokens)
{
Backchannel.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", tokens.AccessToken);
properties.RedirectUri = Options.HomePath;
properties.IsPersistent = true;
var appConfig = await configService.GetAppConfigAsync().ConfigureAwait(false);
int expire = appConfig.LoginPolicy.VerificatExpireTime;
if (!string.IsNullOrEmpty(tokens.ExpiresIn) && int.TryParse(tokens.ExpiresIn, out var result))
{
properties.ExpiresUtc = TimeProvider.System.GetUtcNow().AddSeconds(result);
expire = (int)(result / 60.0);
}
var user = await Options.HandleUserInfoAsync(Context, tokens).ConfigureAwait(false);
var loginEvent = await GetLogin(expire).ConfigureAwait(false);
await UpdateUser(loginEvent).ConfigureAwait(false);
identity.AddClaim(new Claim(ClaimConst.VerificatId, loginEvent.VerificatId.ToString()));
identity.AddClaim(new Claim(ClaimConst.UserId, RoleConst.SuperAdminId.ToString()));
identity.AddClaim(new Claim(ClaimConst.SuperAdmin, "true"));
identity.AddClaim(new Claim(ClaimConst.OrgId, RoleConst.DefaultTenantId.ToString()));
identity.AddClaim(new Claim(ClaimConst.TenantId, RoleConst.DefaultTenantId.ToString()));
var context = new OAuthCreatingTicketContext(
new ClaimsPrincipal(identity),
properties,
Context,
Scheme,
Options,
Backchannel,
tokens,
user
);
context.RunClaimActions();
await Events.CreatingTicket(context).ConfigureAwait(false);
var httpContext = context.HttpContext;
UserAgent? userAgent = null;
var str = httpContext?.Request?.Headers?.UserAgent;
if (!string.IsNullOrEmpty(str))
{
userAgent = userAgentService.Parse(str);
}
var sysOperateLog = new SysOperateLog()
{
Name = this.Scheme.Name,
Category = LogCateGoryEnum.Login,
ExeStatus = true,
OpIp = httpContext.GetRemoteIpAddressToIPv4(),
OpBrowser = userAgent?.Browser,
OpOs = userAgent?.Platform,
OpTime = DateTime.Now,
VerificatId = loginEvent.VerificatId,
OpAccount = Options.GetName(user),
ReqMethod = "OAuth",
ReqUrl = string.Empty,
ResultJson = string.Empty,
ClassName = nameof(AdminOAuthHandler<TOptions>),
MethodName = string.Empty,
ParamJson = string.Empty,
};
_operateLogMessageQueue.Enqueue(sysOperateLog);
return new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name);
}
private async Task<LoginEvent> GetLogin(int expire)
{
var sysUser = await sysUserService.GetUserByIdAsync(RoleConst.SuperAdminId).ConfigureAwait(false);//获取用户信息
var loginEvent = new LoginEvent
{
Ip = appService.RemoteIpAddress,
Device = appService.UserAgent?.Platform ?? "Unknown",
Expire = expire,
SysUser = sysUser,
VerificatId = CommonUtils.GetSingleId()
};
//获取verificat列表
var tokenTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
//生成verificat信息
var verificatInfo = new VerificatInfo
{
Device = loginEvent.Device ?? "Unknown",
Expire = loginEvent.Expire,
VerificatTimeout = tokenTimeout,
Id = loginEvent.VerificatId,
UserId = loginEvent.SysUser.Id,
LoginIp = loginEvent.Ip,
LoginTime = loginEvent.DateTime
};
//添加到verificat列表
verificatInfoService.Add(verificatInfo);
return loginEvent;
}
/// <summary>
/// 登录事件
/// </summary>
/// <param name="loginEvent"></param>
/// <returns></returns>
private async Task UpdateUser(LoginEvent loginEvent)
{
var sysUser = loginEvent.SysUser;
#region /
var key = CacheConst.Cache_LoginErrorCount + sysUser.Account;//获取登录错误次数Key值
App.CacheService.Remove(key);//移除登录错误次数
//获取用户verificat列表
var userToken = verificatInfoService.GetOne(loginEvent.VerificatId);
#endregion /
#region ,
sysUser.LastLoginIp = sysUser.LatestLoginIp;
sysUser.LastLoginTime = sysUser.LatestLoginTime;
sysUser.LatestLoginIp = loginEvent.Ip;
sysUser.LatestLoginTime = loginEvent.DateTime;
#endregion ,
using var db = DbContext.GetDB<SysUser>();
//更新用户登录信息
if (await db.UpdateableT(sysUser).UpdateColumns(it => new
{
it.LastLoginIp,
it.LastLoginTime,
it.LatestLoginIp,
it.LatestLoginTime,
}).ExecuteCommandAsync().ConfigureAwait(false) > 0)
App.CacheService.HashAdd(CacheConst.Cache_SysUser, sysUser.Id.ToString(), sysUser);//更新Cache信息
}
}
/// <summary>自定义 Token 异常</summary>
public class OAuthTokenException : Exception
{
public OAuthTokenException() : base()
{
}
public OAuthTokenException(string? message, Exception? innerException) : base(message, innerException)
{
}
public OAuthTokenException(string? message) : base(message)
{
}
}

View File

@@ -0,0 +1,84 @@
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
/// <summary>OAuthOptions 配置类</summary>
public abstract class AdminOAuthOptions : OAuthOptions
{
/// <summary>默认构造函数</summary>
protected AdminOAuthOptions()
{
ConfigureClaims();
this.Events.OnRemoteFailure = context =>
{
var redirectUri = string.IsNullOrEmpty(HomePath) ? "/" : HomePath;
context.Response.Redirect(redirectUri);
context.HandleResponse();
return Task.CompletedTask;
};
Backchannel = new HttpClient(new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
});
Backchannel.DefaultRequestHeaders.UserAgent.Add(
new ProductInfoHeaderValue("ThingsGateway", "1.0"));
}
/// <summary>配置 Claims 映射</summary>
protected virtual void ConfigureClaims()
{
}
public virtual string GetName(JsonElement element)
{
JsonElement.ObjectEnumerator target = element.EnumerateObject();
return target.TryGetValue("name");
}
/// <summary>获得/设置 登陆后首页</summary>
public string HomePath { get; set; } = "/";
/// <summary>处理用户信息方法</summary>
public virtual async Task<JsonElement> HandleUserInfoAsync(HttpContext context, OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, BuildUserInfoUrl(tokens));
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await Backchannel.SendAsync(request, context.RequestAborted).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return JsonDocument.Parse(content).RootElement;
}
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
}
/// <summary>生成用户信息请求地址方法</summary>
protected virtual string BuildUserInfoUrl(OAuthTokenResponse tokens)
{
return QueryHelpers.AddQueryString(UserInformationEndpoint, new Dictionary<string, string>
{
{ "access_token", tokens.AccessToken }
});
}
/// <summary>生成错误信息方法</summary>
protected async Task<string> Display(HttpResponseMessage response)
{
var output = new StringBuilder();
output.Append($"Status: {response.StatusCode}; ");
output.Append($"Headers: {response.Headers}; ");
output.Append($"Body: {await response.Content.ReadAsStringAsync().ConfigureAwait(false)};");
return output.ToString();
}
}

View File

@@ -0,0 +1,115 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.WebUtilities;
using System.Net.Http.Headers;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GiteeOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
this.SignInScheme = ClaimConst.Scheme;
this.AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
this.TokenEndpoint = "https://gitee.com/oauth/token";
this.UserInformationEndpoint = "https://gitee.com/api/v5/user";
this.HomePath = "/";
this.CallbackPath = "/signin-gitee";
Scope.Add("user_info");
Scope.Add("projects");
Events.OnCreatingTicket = async context => await HandlerGiteeStarredUrl(context).ConfigureAwait(false);
Events.OnRedirectToAuthorizationEndpoint = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
/// <summary>刷新 Token 方法</summary>
protected virtual async Task<OAuthTokenResponse> RefreshTokenAsync(TicketReceivedContext ticketReceivedContext, string refreshToken)
{
var query = new Dictionary<string, string>
{
{ "refresh_token", refreshToken },
{ "grant_type", "refresh_token" }
};
var request = new HttpRequestMessage(HttpMethod.Post, QueryHelpers.AddQueryString(TokenEndpoint, query));
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await Backchannel.SendAsync(request, ticketReceivedContext.HttpContext.RequestAborted).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return OAuthTokenResponse.Success(JsonDocument.Parse(content));
}
return OAuthTokenResponse.Failed(new OAuthTokenException($"OAuth token endpoint failure: {await Display(response).ConfigureAwait(false)}"));
}
public override string GetName(JsonElement element)
{
JsonElement.ObjectEnumerator target = element.EnumerateObject();
return target.TryGetValue("name");
}
private async Task HandlerGiteeStarredUrl(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
var uri = $"https://gitee.com/api/v5/user/starred/{repoFullName}";
var queryString = new Dictionary<string, string>
{
{ "access_token", context.AccessToken }
};
var request = new HttpRequestMessage(HttpMethod.Get, QueryHelpers.AddQueryString(uri, queryString))
{
Headers = { Accept = { new MediaTypeWithQualityHeaderValue("application/json") } }
};
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://gitee.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
//throw new Exception($"Failed to star repository: {response.StatusCode}, {content}");
}
}
protected override void ConfigureClaims()
{
ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url");
ClaimActions.MapJsonKey(ClaimConst.Account, "name");
base.ConfigureClaims();
}
}

View File

@@ -0,0 +1,7 @@
namespace ThingsGateway.Admin.Application;
public class GiteeOAuthSettings
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}

View File

@@ -0,0 +1,112 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using ThingsGateway.NewLife.Log;
namespace ThingsGateway.Admin.Application;
public class GitHubOAuthOptions : AdminOAuthOptions
{
INoticeService _noticeService;
IVerificatInfoService _verificatInfoService;
public GitHubOAuthOptions() : base()
{
_noticeService = App.GetService<INoticeService>();
_verificatInfoService = App.GetService<IVerificatInfoService>();
SignInScheme = ClaimConst.Scheme;
AuthorizationEndpoint = "https://github.com/login/oauth/authorize";
TokenEndpoint = "https://github.com/login/oauth/access_token";
UserInformationEndpoint = "https://api.github.com/user";
HomePath = "/";
CallbackPath = "/signin-github";
Scope.Add("read:user");
Scope.Add("public_repo"); // 需要用于 Star 仓库
Events.OnCreatingTicket = async context => await HandleGitHubStarAsync(context).ConfigureAwait(false);
Events.OnRedirectToAuthorizationEndpoint = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
Events.OnRemoteFailure = context =>
{
XTrace.WriteException(context.Failure);
return Task.CompletedTask;
};
}
protected override void ConfigureClaims()
{
ClaimActions.MapJsonKey(ClaimConst.AvatarUrl, "avatar_url");
ClaimActions.MapJsonKey(ClaimConst.Account, "login");
base.ConfigureClaims();
}
public override string GetName(JsonElement element)
{
if (element.TryGetProperty("login", out var loginProp))
{
return loginProp.GetString() ?? string.Empty;
}
return string.Empty;
}
private async Task HandleGitHubStarAsync(OAuthCreatingTicketContext context, string repoFullName = "ThingsGateway/ThingsGateway")
{
if (string.IsNullOrWhiteSpace(context.AccessToken))
throw new InvalidOperationException("Access token is missing.");
var request = new HttpRequestMessage(HttpMethod.Put, $"https://api.github.com/user/starred/{repoFullName}")
{
Headers =
{
Accept = { new MediaTypeWithQualityHeaderValue("application/vnd.github+json") },
Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken),
},
Content = new StringContent(string.Empty) // GitHub Star 接口需要空内容
};
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
var response = await context.Backchannel.SendAsync(request, context.HttpContext.RequestAborted).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var id = context.Identity.Claims.FirstOrDefault(a => a.Type == ClaimConst.VerificatId).Value;
var verificatInfoIds = _verificatInfoService.GetOne(id.ToLong());
_ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
await _noticeService.NavigationMesage(verificatInfoIds.ClientIds, "https://github.com/ThingsGateway/ThingsGateway", "创作不易如有帮助请star仓库").ConfigureAwait(false);
});
}
}
/// <summary>处理用户信息方法</summary>
public override async Task<JsonElement> HandleUserInfoAsync(HttpContext context, OAuthTokenResponse tokens)
{
var request = new HttpRequestMessage(HttpMethod.Get, UserInformationEndpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.github+json"));
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ThingsGateway", "1.0")); // GitHub API 要求 User-Agent
var response = await Backchannel.SendAsync(request, context.RequestAborted).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
return JsonDocument.Parse(content).RootElement;
}
throw new OAuthTokenException($"OAuth user info endpoint failure: {await Display(response).ConfigureAwait(false)}");
}
}

View File

@@ -0,0 +1,5 @@
namespace ThingsGateway.Admin.Application;
public class GithubOAuthSettings : GiteeOAuthSettings
{
}

View File

@@ -0,0 +1,11 @@
using System.Text.Json;
namespace ThingsGateway.Admin.Application;
public static class OAuthUserExtensions
{
public static string TryGetValue(this JsonElement.ObjectEnumerator target, string propertyName)
{
return target.FirstOrDefault<JsonProperty>((Func<JsonProperty, bool>)(t => t.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase))).Value.ToString() ?? string.Empty;
}
}

View File

@@ -0,0 +1,18 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.ConfigurableOptions;
namespace ThingsGateway.Admin.Application;
public sealed class AdminLogOptions : IConfigurableOptions
{
public int OperateLogDaysAgo { get; set; }
}

View File

@@ -0,0 +1,59 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.ConfigurableOptions;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 邮件配置选项
/// </summary>
public sealed class EmailOptions : IConfigurableOptions
{
/// <summary>
/// 主机
/// </summary>
public string Host { get; set; }
/// <summary>
/// 端口
/// </summary>
public int Port { get; set; }
/// <summary>
/// 默认发件者邮箱
/// </summary>
public string DefaultFromEmail { get; set; }
/// <summary>
/// 默认接收人邮箱
/// </summary>
public string DefaultToEmail { get; set; }
/// <summary>
/// 启用SSL
/// </summary>
public bool EnableSsl { get; set; }
/// <summary>
/// 邮箱账号
/// </summary>
public string UserName { get; set; }
/// <summary>
/// 邮箱密码
/// </summary>
public string Password { get; set; }
/// <summary>
/// 默认邮件标题
/// </summary>
public string DefaultFromName { get; set; }
}

View File

@@ -0,0 +1,34 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.ConfigurableOptions;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 历史硬件信息
/// </summary>
public class HardwareInfoOptions : IConfigurableOptions
{
/// <summary>
/// 启用
/// </summary>
public bool Enable { get; set; } = true;
/// <summary>
/// 历史保存间隔
/// </summary>
public int HistoryInterval { get; set; } = 60000;
/// <summary>
/// 历史保留天数
/// </summary>
public int DaysAgo { get; set; } = 7;
}

View File

@@ -0,0 +1,21 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.ConfigurableOptions;
namespace ThingsGateway.Admin.Application;
public sealed class TenantOptions : IConfigurableOptions
{
/// <summary>
/// 启用
/// </summary>
public bool Enable { get; set; }
}

View File

@@ -0,0 +1,278 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using ThingsGateway.Authorization;
using ThingsGateway.DataEncryption;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class BlazorAuthenticationHandler : AppAuthorizeHandler
{
private readonly ISysDictService _sysDictService;
private readonly ISysRoleService _sysRoleService;
private readonly ISysUserService _sysUserService;
public BlazorAuthenticationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService)
{
_sysUserService = sysUserService;
_sysRoleService = sysRoleService;
_sysDictService = sysDictService;
}
/// <inheritdoc/>
public override async Task HandleAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
{
var isAuthenticated = context.User.Identity?.IsAuthenticated;
if (isAuthenticated == true)
{
if (await CheckVerificatFromCacheAsync(context).ConfigureAwait(false))
{
await AuthorizeHandleAsync(context).ConfigureAwait(false);
}
else
{
await Fail(context).ConfigureAwait(false);
}
}
else
{
await Fail(context).ConfigureAwait(false);// 授权失败
}
}
static async Task Fail(AuthorizationHandlerContext context)
{
var verificatId = UserManager.VerificatId;
var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId, false);
if (App.HttpContext != null)
{
var identity = new ClaimsIdentity();
App.HttpContext.User = new ClaimsPrincipal(identity);
}
context.Fail(); // 授权失败
if (verificatInfo != null)
await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false);
DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext();
if (currentHttpContext == null)
return;
currentHttpContext.Response.StatusCode = 401; //返回401给授权筛选器用
currentHttpContext.SignoutToSwagger();
await currentHttpContext.SignOutAsync().ConfigureAwait(false);
}
/// <inheritdoc/>
public override async Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
{
var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong(0) ?? 0;
var user = await _sysUserService.GetUserByIdAsync(userId).ConfigureAwait(false);
if (context.Resource is Microsoft.AspNetCore.Components.RouteData routeData)
{
var roles = await _sysRoleService.GetRoleListByUserIdAsync(userId).ConfigureAwait(false);
//这里鉴别用户使能状态
if (user?.Status != true)
{
return false;
}
//超级管理员都能访问
var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value.ToBoolean();
if (isSuperAdmin == true) return true;
// 获取超级管理员特性
var superAdminAttr = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(SuperAdminAttribute));
if (superAdminAttr != null) //如果是超级管理员才能访问的接口
{
return false; //直接没权限
}
//获取角色授权特性
var isRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(RolePermissionAttribute));
if (isRolePermission != null)
{
//获取忽略角色授权特性
var isIgnoreRolePermission = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(IgnoreRolePermissionAttribute));
if (isIgnoreRolePermission == null)
{
// 路由名称
var routeName = routeData.PageType.CustomAttributes.FirstOrDefault(x =>
x.AttributeType == typeof(Microsoft.AspNetCore.Components.RouteAttribute))?.ConstructorArguments?[0].Value as string;
if (routeName == null) return true;
if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败
return false;
else
return true;
}
else
{
return true;
}
}
else
{
return true;
}
}
else
{
//这里鉴别用户使能状态
if (user?.Status != true)
{
return false;
}
//超级管理员都能访问
var isSuperAdmin = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.SuperAdmin)?.Value?.ToBoolean();
if (isSuperAdmin == true) return true;
if (httpContext == null)
{
//非API请求
return true;
}
var superAdminAttr = httpContext.GetMetadata<SuperAdminAttribute>();
if (superAdminAttr != null) //如果是超级管理员才能访问的接口
{
//获取忽略超级管理员特性
var ignoreSpuerAdmin = httpContext.GetMetadata<IgnoreSuperAdminAttribute>();
if (ignoreSpuerAdmin == null && isSuperAdmin != true) //如果只能超级管理员访问并且用户不是超级管理员
return false; //直接没权限
}
//获取角色授权特性
var isRolePermission = httpContext.GetMetadata<RolePermissionAttribute>();
if (isRolePermission != null)
{
//获取忽略角色授权特性
var ignoreRolePermission = httpContext.GetMetadata<IgnoreRolePermissionAttribute>();
if (ignoreRolePermission == null)
{
// 路由名称
var routeName = httpContext.Request.Path.Value;
if (routeName == null) return true;
if ((!user.PermissionCodeList.Contains(routeName.CutStart("/")) && !user.PermissionCodeList.Contains(routeName))) //如果当前路由信息不包含在角色授权路由列表中则认证失败
return false;
else
{
return true; //没有用户信息则返回认证失败
}
}
}
else
{
return true;
}
}
return true;
}
/// <summary>
/// 检查 BearerToken/Cookie 有效性
/// </summary>
/// <param name="context">DefaultHttpContext</param>
/// <returns></returns>
private async Task<bool> CheckVerificatFromCacheAsync(AuthorizationHandlerContext context)
{
DefaultHttpContext currentHttpContext = context.GetCurrentHttpContext();
var userId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong();
var verificatId = context.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong();
var expire = (await _sysDictService.GetAppConfigAsync().ConfigureAwait(false)).LoginPolicy.VerificatExpireTime;
if (currentHttpContext == null)
{
return CheckVerificat(userId, verificatId);
}
else
{
if (await JWTEncryption.AutoRefreshToken(context, currentHttpContext, expire, expire * 2).ConfigureAwait(false))
{
//var token = JWTEncryption.GetJwtBearerToken(currentHttpContext); //获取当前token
var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息
if (verificatInfo != null)
{
if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1))
{
verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间
VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache
}
return true;
}
else
{
return false;
}
}
else
{
//失败
return false;
}
}
}
private static IVerificatInfoService _verificatInfoService;
private static IVerificatInfoService VerificatInfoService
{
get
{
if (_verificatInfoService == null)
_verificatInfoService = App.GetService<IVerificatInfoService>();
return _verificatInfoService;
}
}
public static bool CheckVerificat(long? userId, long? verificatId, bool autoUpdate = true)
{
var verificatInfo = userId != null ? VerificatInfoService.GetOne(verificatId ?? 0, false) : null;//获取token信息
if (verificatInfo != null)
{
if (verificatInfo.VerificatTimeout < DateTime.Now.AddMinutes(1))
{
if (!autoUpdate && verificatInfo.VerificatTimeout < DateTime.Now)
return false;
if (verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire) < DateTime.Now)
{
return false;
}
if (autoUpdate)
{
verificatInfo.VerificatTimeout = verificatInfo.VerificatTimeout.AddMinutes(verificatInfo.Expire); //新的过期时间
VerificatInfoService.Update(verificatInfo); //更新tokne信息到cache
}
//无法在server中刷新cookies单页面应用会一直保持登录状态所以这里不需要刷新cookies但是F5刷新后会重新登录
}
return true;
}
else
{
return false;
}
}
}

View File

@@ -0,0 +1,105 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class BlazorHybridAuthenticationStateProvider : AuthenticationStateProvider
{
private readonly IAppService _appService;
private CancellationTokenSource _loopCancellationTokenSource = new CancellationTokenSource();
public BlazorHybridAuthenticationStateProvider(IAppService appService)
{
_appService = appService;
}
private AuthenticationState AuthenticationState = new AuthenticationState(new ClaimsPrincipal());
public void UserChanged(ClaimsPrincipal user)
{
AuthenticationState = new AuthenticationState(user);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
var oldCancellationTokenSource = _loopCancellationTokenSource;
if (oldCancellationTokenSource is not null)
{
oldCancellationTokenSource.Cancel();
oldCancellationTokenSource.Dispose();
}
_loopCancellationTokenSource = new CancellationTokenSource();
if (AuthenticationState.User?.Identity?.IsAuthenticated == true)
_ = RevalidationLoop(_loopCancellationTokenSource.Token);
}
private async Task RevalidationLoop(CancellationToken cancellationToken)
{
try
{
if (AuthenticationState.User.Identity?.IsAuthenticated == true)
{
while (!cancellationToken.IsCancellationRequested)
{
bool isValid = false;
try
{
await Task.Delay(30000, cancellationToken).ConfigureAwait(false);
isValid = await ValidateAuthenticationStateAsync(AuthenticationState, cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
break;
}
if (!isValid)
{
ForceSignOut();
break;
}
}
}
}
catch
{
ForceSignOut();
}
}
protected virtual async Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
{
var userId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong();
var verificatId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong();
var result = BlazorAuthenticationHandler.CheckVerificat(userId, verificatId, false);
if (!result)
{
var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId ?? 0, false);
if (App.HttpContext != null)
{
var identity = new ClaimsIdentity();
App.HttpContext.User = new ClaimsPrincipal(identity);
}
if (verificatInfo != null)
await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false);
}
return result;
}
private void ForceSignOut()
{
var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
UserChanged(anonymousUser);
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(AuthenticationState);
}
}

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class BlazorHybridAuthorizationHandler : BlazorAuthenticationHandler
{
public BlazorHybridAuthorizationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService) : base(sysUserService, sysRoleService, sysDictService)
{
}
}

View File

@@ -0,0 +1,19 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class BlazorServerAuthenticationHandler : BlazorAuthenticationHandler
{
public BlazorServerAuthenticationHandler(ISysUserService sysUserService, ISysRoleService sysRoleService, ISysDictService sysDictService) : base(sysUserService, sysRoleService, sysDictService)
{
}
}

View File

@@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Microsoft.Extensions.Logging;
using System.Security.Claims;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
public class BlazorServerAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider
{
private readonly IAppService _appService;
public BlazorServerAuthenticationStateProvider(ILoggerFactory loggerFactory, IAppService appService) : base(loggerFactory)
{
_appService = appService;
}
protected override TimeSpan RevalidationInterval => TimeSpan.FromSeconds(30);
protected override async Task<bool> ValidateAuthenticationStateAsync(AuthenticationState authenticationState, CancellationToken cancellationToken)
{
var userId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.UserId)?.Value?.ToLong();
var verificatId = authenticationState.User.Claims.FirstOrDefault(it => it.Type == ClaimConst.VerificatId)?.Value?.ToLong();
var result = BlazorAuthenticationHandler.CheckVerificat(userId, verificatId, false);
if (!result)
{
var verificatInfo = App.GetService<IVerificatInfoService>().GetOne(verificatId ?? 0, false);
if (App.HttpContext != null)
{
var identity = new ClaimsIdentity();
App.HttpContext.User = new ClaimsPrincipal(identity);
}
if (verificatInfo != null)
await App.GetService<INoticeService>().UserLoginOut(verificatInfo.ClientIds, App.CreateLocalizerByType(typeof(BlazorAuthenticationHandler))["UserExpire"]).ConfigureAwait(false);
}
return result;
}
}

View File

@@ -0,0 +1,96 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ThingsGateway.DataValidation;
using ThingsGateway.FriendlyException;
using ThingsGateway.UnifyResult;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 规范化RESTful风格返回值
/// </summary>
[UnifyModel(typeof(UnifyResult<>))]
public class UnifyResultProvider : IUnifyResultProvider
{
private static IStringLocalizer Localizer = App.CreateLocalizerByType(typeof(UnifyResultProvider))!;
/// <summary>
/// 状态码响应拦截
/// </summary>
public async Task OnResponseStatusCodes(HttpContext context, int statusCode, UnifyResultSettingsOptions unifyResultSettings = null)
{
switch (statusCode)
{
// 处理 401 状态码
case StatusCodes.Status401Unauthorized:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, Localizer["TokenOver"].Value)).ConfigureAwait(false);
break;
// 处理 403 状态码
case StatusCodes.Status403Forbidden:
await context.Response.WriteAsJsonAsync(RESTfulResult(statusCode, false, default, Localizer["NoPermission"].Value)).ConfigureAwait(false);
break;
default: break;
}
}
/// <summary>
/// 成功返回
/// </summary>
/// <param name="context"></param>
/// <param name="data"></param>
/// <returns></returns>
public IActionResult OnSucceeded(ActionExecutedContext context, object? data)
{
return new JsonResult(RESTfulResult(StatusCodes.Status200OK, true, data));
}
/// <summary>
/// 返回 RESTful 风格结果集
/// </summary>
/// <param name="statusCode">状态码</param>
/// <param name="succeeded">是否成功</param>
/// <param name="data">数据</param>
/// <param name="errors">错误信息</param>
/// <returns></returns>
private static UnifyResult<object> RESTfulResult(int statusCode, bool succeeded = default, object? data = default, object? errors = default)
{
return new UnifyResult<object>
{
Code = statusCode,
Msg = statusCode == StatusCodes.Status200OK ? "Success" : errors,
Data = data,
Time = DateTime.Now,
};
}
public IActionResult OnAuthorizeException(DefaultHttpContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors ?? metadata.Exception?.Message)
, UnifyContext.GetSerializerSettings(context));
}
public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode, data: metadata.Data, errors: metadata.Errors ?? metadata.Exception?.Message)
, UnifyContext.GetSerializerSettings(context));
}
public IActionResult OnValidateFailed(ActionExecutingContext context, ValidationMetadata metadata)
{
return new JsonResult(RESTfulResult(metadata.StatusCode ?? StatusCodes.Status400BadRequest, data: metadata.Data, errors: metadata.ValidationResult) // 如果需要只显示第一条错误修改为errors: metadata.FirstErrorMessage
, UnifyContext.GetSerializerSettings(context));
}
}

View File

@@ -0,0 +1,124 @@
{
"RECORDS": [
{
"Id": 252875263003121,
"DictType": "System",
"Category": "LoginPolicy",
"Name": "VerificatExpireTime",
"Code": "14400",
"Remark": "Verificat过期时间(分)",
"IsDelete": "0",
"SortCode": 9
},
{
"Id": 252875263003720,
"DictType": "System",
"Category": "PagePolicy",
"Name": "Shortcuts",
"Code": "[100]",
"Remark": "系统默认工作台数据",
"IsDelete": "0",
"SortCode": 1
},
{
"Id": 252875263003728,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "DefaultPassword",
"Code": "111111",
"Remark": "默认用户密码",
"IsDelete": "0",
"SortCode": 1
},
{
"Id": 364564896825413,
"DictType": "System",
"Category": "LoginPolicy",
"Name": "SingleOpen",
"Code": "False",
"Remark": "单用户登录开关",
"IsDelete": "0",
"SortCode": 1
},
{
"Id": 432079540875333,
"DictType": "System",
"Category": "LoginPolicy",
"Name": "ErrorCount",
"Code": "10",
"Remark": "登录错误次数",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432079718375493,
"DictType": "System",
"Category": "LoginPolicy",
"Name": "ErrorResetTime",
"Code": "1",
"Remark": "错误重置时间",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432079850803269,
"DictType": "System",
"Category": "LoginPolicy",
"Name": "ErrorLockTime",
"Code": "2",
"Remark": "登录错误锁定时长",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432111701160005,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "PasswordMinLen",
"Code": "6",
"Remark": "密码最小长度",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432112135159877,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "PasswordContainNum",
"Code": "False",
"Remark": "包含数字",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432112321953861,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "PasswordContainLower",
"Code": "False",
"Remark": "包含小写字母",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432112380907589,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "PasswordContainUpper",
"Code": "False",
"Remark": "包含大写字母",
"IsDelete": "0",
"SortCode": 99
},
{
"Id": 432112495247429,
"DictType": "System",
"Category": "PasswordPolicy",
"Name": "PasswordContainChar",
"Code": "False",
"Remark": "包含特殊字符",
"IsDelete": "0",
"SortCode": 99
}
]
}

View File

@@ -0,0 +1,11 @@
{
"RECORDS": [
{
"Id": 503217527844933,
"Category": 0,
"ObjectId": 212725263002001,
"TargetId": "212725263001001",
"ExtJson": null
}
]
}

View File

@@ -0,0 +1,288 @@
{
"RECORDS": [
{
"Id": 2,
"ParentId": 0,
"Title": "系统管理",
"Code": "System",
"Category": "MODULE",
"Target": "_self",
"Href": "",
"Icon": "fa-solid fa-house-user",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 100001,
"ParentId": 0,
"Module": 2,
"Title": "权限管理",
"Code": "System",
"NavLinkMatch": "Prefix",
"Category": "MENU",
"Target": "_self",
"Href": null,
"Icon": "fa-solid fa-users-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 100,
"ExtJson": null
},
{
"Id": 100002,
"ParentId": 0,
"Module": 2,
"Title": "系统运维",
"NavLinkMatch": "Prefix",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": null,
"Icon": "fa-solid fa-file-circle-check",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 101,
"ExtJson": null
},
{
"Id": 100001001,
"Module": 2,
"ParentId": 100001,
"Title": "用户管理",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/user",
"Icon": "fa-solid fa-user-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 100001005,
"Module": 2,
"ParentId": 100001,
"Title": "机构管理",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/org",
"Icon": "fa-solid fa-user-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 100001004,
"Module": 2,
"ParentId": 100001,
"Title": "职位管理",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/position",
"Icon": "fa-solid fa-user-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 100001002,
"Module": 2,
"ParentId": 100001,
"Title": "角色管理",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"NavLinkMatch": "All",
"Href": "/admin/role",
"Icon": "fa-solid fa-users-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 100001003,
"Module": 2,
"ParentId": 100001,
"Title": "菜单管理",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/resource",
"NavLinkMatch": "All",
"Icon": "fa-solid fa-bars",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 100002001,
"ParentId": 100002,
"Module": 2,
"Title": "系统配置",
"Code": "System",
"Category": "MENU",
"NavLinkMatch": "All",
"Target": "_self",
"Href": "/admin/config",
"Icon": "fa-solid fa-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 100002002,
"Module": 2,
"ParentId": 100002,
"Title": "字典管理",
"Code": "System",
"NavLinkMatch": "All",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/dict",
"Icon": "fa-solid fa-gear",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 100002003,
"Module": 2,
"ParentId": 100002,
"Title": "操作日志",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/oplog",
"Icon": "fa-solid fa-edit",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 100002004,
"Module": 2,
"ParentId": 100002,
"Title": "会话管理",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/session",
"Icon": "fa-solid fa-person-circle-check",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 4,
"ExtJson": null
},
{
"Id": 100002005,
"Module": 2,
"ParentId": 100002,
"Title": "硬件信息",
"NavLinkMatch": "All",
"Code": "System",
"Category": "MENU",
"Target": "_self",
"Href": "/admin/hardwareinfo",
"Icon": "fas fa-server",
"CreateTime": null,
"CreateUser": null,
"CreateUserId": 0,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 4,
"ExtJson": null
}
]
}

View File

@@ -0,0 +1,845 @@
{
"RECORDS": [
{
"Id": 501949744332869,
"ParentId": 100001001,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502949744332869,
"ParentId": 100001001,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502949744332870,
"ParentId": 100001001,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 502949744332871,
"ParentId": 100001001,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 502950809124933,
"ParentId": 100001001,
"Module": 2,
"Title": "重置密码",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:52:06.903",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502950980558917,
"ParentId": 100001001,
"Module": 2,
"Title": "授权角色",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:52:48.757",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502951076454469,
"ParentId": 100001001,
"Module": 2,
"Title": "授权资源",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:12.17",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": "11/1/2024 14:55:44.917",
"UpdateUser": "SuperAdmin",
"UpdateUserId": 212725263002001,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502951133163589,
"ParentId": 100001001,
"Module": 2,
"Title": "授权Api",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:26.017",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": "11/1/2024 15:01:16.98",
"UpdateUser": "SuperAdmin",
"UpdateUserId": 212725263002001,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 513951271813189,
"ParentId": 100001002,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:59.867",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502951271813189,
"ParentId": 100001002,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:59.867",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502951271813190,
"ParentId": 100001002,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:59.867",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 502951271813191,
"ParentId": 100001002,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:53:59.867",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 502951661793349,
"ParentId": 100001002,
"Module": 2,
"Title": "授权资源",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:55:35.077",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502952928694341,
"ParentId": 100001002,
"Module": 2,
"Title": "授权用户",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:00:44.377",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502952990781509,
"ParentId": 100001002,
"Module": 2,
"Title": "授权Api",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:00:59.537",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": "11/1/2024 15:01:10.2",
"UpdateUser": "SuperAdmin",
"UpdateUserId": 212725263002001,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 512953216376901,
"ParentId": 100001003,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:01:54.617",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502953216376901,
"ParentId": 100001003,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:01:54.617",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502953216376902,
"ParentId": 100001003,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:01:54.617",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 502953216376903,
"ParentId": 100001003,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:01:54.617",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 512953961062469,
"ParentId": 100002002,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:04:56.42",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502953961062469,
"ParentId": 100002002,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:04:56.42",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502953961062470,
"ParentId": 100002002,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:04:56.42",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 502953961062471,
"ParentId": 100002002,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:04:56.42",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 532953961062469,
"ParentId": 100002001,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:04:56.42",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 502954345615429,
"ParentId": 100002001,
"Module": 2,
"Code": "System",
"Title": "密码策略保存",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:06:30.307",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502954382938181,
"ParentId": 100002001,
"Module": 2,
"Code": "System",
"Title": "登录策略保存",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:06:39.417",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502954382931181,
"ParentId": 100002001,
"Module": 2,
"Code": "System",
"Title": "页面策略保存",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:06:39.417",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502954382931281,
"ParentId": 100002001,
"Module": 2,
"Title": "网站策略保存",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:06:39.417",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 505954686193733,
"ParentId": 100002003,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:07:53.453",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502954686193733,
"ParentId": 100002003,
"Module": 2,
"Title": "日志清空",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:07:53.453",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 501954811879493,
"ParentId": 100002004,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:08:24.14",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 502954811879493,
"ParentId": 100002004,
"Module": 2,
"Title": "会话强退",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 15:08:24.14",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 0,
"ExtJson": null
},
{
"Id": 401949744332869,
"ParentId": 100001005,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 402949744332869,
"ParentId": 100001005,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 402949744332870,
"ParentId": 100001005,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 402949744332871,
"ParentId": 100001005,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
},
{
"Id": 301949744332869,
"ParentId": 100001004,
"Module": 2,
"Title": "查询",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 302949744332869,
"ParentId": 100001004,
"Module": 2,
"Title": "新增",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 1,
"ExtJson": null
},
{
"Id": 302949744332870,
"ParentId": 100001004,
"Module": 2,
"Title": "编辑",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 2,
"ExtJson": null
},
{
"Id": 302949744332871,
"ParentId": 100001004,
"Module": 2,
"Title": "删除",
"Code": "System",
"Category": "Button",
"Href": null,
"Icon": null,
"CreateTime": "11/1/2024 14:47:46.973",
"CreateUser": "SuperAdmin",
"CreateUserId": 212725263002001,
"IsDelete": "0",
"UpdateTime": null,
"UpdateUser": null,
"UpdateUserId": null,
"SortCode": 3,
"ExtJson": null
}
]
}

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 系统配置种子数据
/// </summary>
public class SysDictSeedData : ISqlSugarEntitySeedData<SysDict>
{
/// <inheritdoc/>
public IEnumerable<SysDict> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysDict>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_dict.json"));
var assembly = GetType().Assembly;
return SeedDataUtil.GetSeedDataByJson<SysDict>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_dict.json")).Concat(data);
}
}

View File

@@ -0,0 +1,41 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 职位表种子数据
/// </summary>
public class SysOrgSeedData : ISqlSugarEntitySeedData<SysOrg>
{
/// <inheritdoc/>
public IEnumerable<SysOrg> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysOrg>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_org.json"));
var assembly = GetType().Assembly;
return new List<SysOrg>()
{
new SysOrg()
{
Id=RoleConst.DefaultTenantId,
Status=true,
IsDelete=false,
DirectorId=RoleConst.SuperAdminId,
Name="Default",
Code="Diego",
Category=OrgEnum.COMPANY,
CreateUserId=RoleConst.SuperAdminId,
Names="Default",
SortCode=0
}
}.Concat(SeedDataUtil.GetSeedDataByJson<SysOrg>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_org.json")).Concat(data));
}
}

View File

@@ -0,0 +1,40 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 职位表种子数据
/// </summary>
public class SysPositionSeedData : ISqlSugarEntitySeedData<SysPosition>
{
/// <inheritdoc/>
public IEnumerable<SysPosition> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysPosition>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_position.json"));
var assembly = GetType().Assembly;
return new List<SysPosition>()
{
new SysPosition()
{
Id=RoleConst.DefaultPositionId,
Status=true,
IsDelete=false,
Name="管理员",
OrgId=RoleConst.DefaultTenantId,
Code="ThingsGateway",
Category=PositionCategoryEnum.HIGH,
CreateUserId=RoleConst.SuperAdminId,
SortCode=0
}
}.Concat(SeedDataUtil.GetSeedDataByJson<SysPosition>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_position.json")).Concat(data));
}
}

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Application;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 关系表种子数据
@@ -18,8 +18,11 @@ public class SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation>
/// <inheritdoc/>
public IEnumerable<SysRelation> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysRelation>(PathExtensions.CombinePathWithOs("SeedData", "Gateway", "seed_gateway_relation.json"));
using var db = DbContext.GetDB<SysRelation>();
if (db.Queryable<SysRelation>().Any(a => a.ObjectId == RoleConst.SuperAdminId))
return Enumerable.Empty<SysRelation>();
var data = SeedDataUtil.GetSeedData<SysRelation>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_relation.json"));
var assembly = GetType().Assembly;
return SeedDataUtil.GetSeedDataByJson<SysRelation>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Gateway.seed_gateway_relation.json")).Concat(data);
return SeedDataUtil.GetSeedDataByJson<SysRelation>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_relation.json")).Concat(data);
}
}

View File

@@ -8,7 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Application;
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 资源表种子数据
@@ -18,10 +18,18 @@ public class SysResourceSeedData : ISqlSugarEntitySeedData<SysResource>
/// <inheritdoc/>
public IEnumerable<SysResource> SeedData()
{
var data1 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Gateway", "seed_gateway_resource.json"));
var data2 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Gateway", "seed_gateway_resourcebutton.json"));
var assembly = GetType().Assembly;
return SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Gateway.seed_gateway_resource.json")).Concat(SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Gateway.seed_gateway_resourcebutton.json"))).Concat(data1).Concat(data2);
var data1 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_resource.json"));
var data2 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_resourcebutton.json"));
var data3 = SeedDataUtil.GetSeedData<SysResource>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_biz_resource.json"));
var assembly = GetType().Assembly;
return SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_resource.json"))
.Concat(SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_resourcebutton.json")))
.Concat(SeedDataUtil.GetSeedDataByJson<SysResource>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_biz_resource.json")))
.Concat(data1)
.Concat(data2)
.Concat(data3)
;
}
}

View File

@@ -0,0 +1,37 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 角色种子数据
/// </summary>
public class SysRoleSeedData : ISqlSugarEntitySeedData<SysRole>
{
/// <inheritdoc/>
public IEnumerable<SysRole> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysRole>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_role.json"));
var assembly = GetType().Assembly;
return new List<SysRole>()
{
new SysRole()
{
Id=RoleConst.SuperAdminRoleId,
Code=RoleConst.SuperAdmin,
Name="超级管理员",
Category=RoleCategoryEnum.Global,
DefaultDataScope=new(),
IsDelete=false,
SortCode=0
}
}.Concat(SeedDataUtil.GetSeedDataByJson<SysRole>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_role.json")).Concat(data));
}
}

View File

@@ -0,0 +1,38 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Admin.Application;
/// <summary>
/// 用户表种子数据
/// </summary>
public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
{
/// <inheritdoc/>
public IEnumerable<SysUser> SeedData()
{
var data = SeedDataUtil.GetSeedData<SysUser>(PathExtensions.CombinePathWithOs("SeedData", "Admin", "seed_sys_user.json"));
var assembly = GetType().Assembly;
return new List<SysUser>()
{
new SysUser()
{
Id=RoleConst.SuperAdminId,
Account=RoleConst.SuperAdmin,
Password="7DA385A25A98388E",
OrgId=RoleConst.DefaultTenantId,
PositionId=RoleConst.DefaultPositionId,
Status=true,
IsDelete=false,
SortCode=0
}
}.Concat(SeedDataUtil.GetSeedDataByJson<SysUser>(SeedDataUtil.GetManifestResourceStream(assembly, "SeedData.Admin.seed_sys_user.json"))).Concat(data);
}
}

View File

@@ -0,0 +1,174 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Controllers;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Reflection;
using ThingsGateway.Common.Extension;
namespace ThingsGateway.Admin.Application;
internal sealed class ApiPermissionService : IApiPermissionService
{
private readonly IApiDescriptionGroupCollectionProvider _apiDescriptionGroupCollectionProvider;
public ApiPermissionService(
IOptions<SwaggerGeneratorOptions> generatorOptions,
IApiDescriptionGroupCollectionProvider apiDescriptionGroupCollectionProvider)
{
_apiDescriptionGroupCollectionProvider = apiDescriptionGroupCollectionProvider;
}
/// <inheritdoc />
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector()
{
var cacheKey = $"{nameof(ApiPermissionTreeSelector)}-{CultureInfo.CurrentUICulture.Name}";
var permissions = App.CacheService.Get<List<OpenApiPermissionTreeSelector>>(cacheKey);
if (permissions == null)
{
permissions = new();
Dictionary<string, OpenApiPermissionTreeSelector> groupOpenApis = new();
var apiDescriptions = _apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items;
foreach (var item1 in apiDescriptions)
{
foreach (var item in item1.Items)
{
if (item.ActionDescriptor is ControllerActionDescriptor controllerActionDescriptor)
{
OpenApiPermissionTreeSelector openApiPermissionTreeSelector = new() { ApiName = controllerActionDescriptor.ControllerName ?? "Default" };
groupOpenApis.TryAdd(openApiPermissionTreeSelector.ApiName, openApiPermissionTreeSelector);
}
}
}
// 获取所有需要数据权限的控制器
var controllerTypes =
App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(RolePermissionAttribute), false));
//foreach (var groupOpenApi in groupOpenApis)
{
foreach (var apiDescriptionGroup in apiDescriptions)
{
var routes = apiDescriptionGroup.Items.Where(api => api.ActionDescriptor is ControllerActionDescriptor);
Dictionary<string, OpenApiPermissionTreeSelector> openApiPermissionTreeSelectorDict = new();
foreach (var route in routes)
{
var actionDesc = (ControllerActionDescriptor)route.ActionDescriptor;
if (!actionDesc.ControllerTypeInfo.CustomAttributes.Any(a => a.AttributeType == typeof(RolePermissionAttribute)))
continue;
var controllerDescription = actionDesc.ControllerTypeInfo.GetTypeDisplayName();
if (openApiPermissionTreeSelectorDict.TryGetValue(actionDesc.ControllerName, out var openApiControllerGroup))
{
}
else
{
openApiControllerGroup = new() { ApiName = controllerDescription, ApiRoute = actionDesc.ControllerName };
openApiPermissionTreeSelectorDict.Add(actionDesc.ControllerName, openApiControllerGroup);
}
var ignoreRolePermission = actionDesc.MethodInfo.CustomAttributes.Any(a => a.AttributeType == typeof(IgnoreRolePermissionAttribute));
if (ignoreRolePermission)
continue;
var routePath = route.RelativePath;
var methodDesc = actionDesc.MethodInfo.DeclaringType.GetMethodDisplayName(actionDesc.MethodInfo.Name);
//添加到权限列表
openApiControllerGroup.Children ??= new();
openApiControllerGroup.Children.Add(new OpenApiPermissionTreeSelector
{
ApiName = methodDesc,
ApiRoute = routePath,
});
}
if (openApiPermissionTreeSelectorDict.Values.Any(a => a.Children.Count > 0))
permissions.AddRange(openApiPermissionTreeSelectorDict.Values);
}
}
App.CacheService.Set(cacheKey, permissions);
}
return permissions;
}
/// <summary>
/// 获取路由地址名称
/// </summary>
/// <param name="controllerName">控制器地址</param>
/// <param name="template">路由名称</param>
/// <returns></returns>
public string GetRouteName(string controllerName, string template)
{
if (!template.StartsWith('/'))
template = "/" + template;//如果路由名称不是/开头则加上/防止控制器没写
if (template.Contains("[controller]"))
{
controllerName = controllerName.Replace("Controller", "");//去掉Controller
controllerName = controllerName.ToLowerCamelCase();//转首字母小写写
template = template.Replace("[controller]", controllerName);//替换[controller]
}
return template;
}
/// <inheritdoc />
public IEnumerable<PermissionTreeSelector> PermissionTreeSelector(IEnumerable<string> routes)
{
List<PermissionTreeSelector> permissions = PermissionTreeSelector();
var hashSet = routes.ToHashSet();
return permissions.Where(a => hashSet.Contains(a.ApiRoute));
}
/// <inheritdoc />
public List<PermissionTreeSelector> PermissionTreeSelector()
{
var cacheKey = $"{nameof(PermissionTreeSelector)}-{CultureInfo.CurrentUICulture.Name}";
var permissions = App.CacheService.GetOrAdd(cacheKey, entry =>
{
List<PermissionTreeSelector> permissions = new();//权限列表
// 获取所有需要数据权限的控制器
var controllerTypes = App.EffectiveTypes.Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
&& u.IsDefined(typeof(AuthorizeAttribute), false)
&& u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false));
foreach (var controller in controllerTypes)
{
//获取数据权限特性
var route = controller.GetCustomAttributes<Microsoft.AspNetCore.Components.RouteAttribute>().FirstOrDefault();
if (route == null) continue;
var apiRoute = GetRouteName(controller.Name, route.Template);//赋值路由名称
var desc = controller.GetTypeDisplayName();
//添加到权限列表
permissions.Add(new PermissionTreeSelector
{
ApiName = desc,
ApiRoute = apiRoute,
});
}
return permissions;
});
return permissions;
}
}

View File

@@ -0,0 +1,23 @@
// ------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
// ------------------------------------------------------------------------------
using ThingsGateway.Admin.Application;
public interface IApiPermissionService
{
/// <summary>
/// 获取API树
/// </summary>
/// <returns></returns>
public List<OpenApiPermissionTreeSelector> ApiPermissionTreeSelector();
string GetRouteName(string controllerName, string template);
List<PermissionTreeSelector> PermissionTreeSelector();
IEnumerable<PermissionTreeSelector> PermissionTreeSelector(IEnumerable<string> routes);
}

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