Compare commits
	
		
			766 Commits
		
	
	
		
			10.11.110.
			...
			4.0.0.4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					c6aec3a1af | ||
| 
						 | 
					22e30f7a62 | ||
| 
						 | 
					57711b8ab5 | ||
| 
						 | 
					90ff1259ea | ||
| 
						 | 
					d88fc5ccd7 | ||
| 
						 | 
					5aaca2aa9c | ||
| 
						 | 
					8b9ca56e17 | ||
| 
						 | 
					e4f3772e6d | ||
| 
						 | 
					d58ec81d20 | ||
| 
						 | 
					415aae44b6 | ||
| 
						 | 
					a533286658 | ||
| 
						 | 
					e59f91cd82 | ||
| 
						 | 
					5f8b85d8a4 | ||
| 
						 | 
					47c7b88436 | ||
| 
						 | 
					90006782f2 | ||
| 
						 | 
					c3d49cbe70 | ||
| 
						 | 
					112323a360 | ||
| 
						 | 
					9d08c90fda | ||
| 
						 | 
					602d24deec | ||
| 
						 | 
					a2b9f66785 | ||
| 
						 | 
					7cbf289b50 | ||
| 
						 | 
					4097da79a5 | ||
| 
						 | 
					91b7ae554f | ||
| 
						 | 
					3121aa2542 | ||
| 
						 | 
					4bf895e6e1 | ||
| 
						 | 
					0c5489e920 | ||
| 
						 | 
					d63c3aaa80 | ||
| 
						 | 
					4f188ea6cc | ||
| 
						 | 
					acb17018ae | ||
| 
						 | 
					2affe2988d | ||
| 
						 | 
					4174dd2206 | ||
| 
						 | 
					e1c492f238 | ||
| 
						 | 
					fb08e34fa3 | ||
| 
						 | 
					a1793a0afe | ||
| 
						 | 
					4da9763b49 | ||
| 
						 | 
					81e0918bd0 | ||
| 
						 | 
					c1e064f06d | ||
| 
						 | 
					1c52be8b47 | ||
| 
						 | 
					bcd82055ca | ||
| 
						 | 
					c47d95d170 | ||
| 
						 | 
					3e62f1ad51 | ||
| 
						 | 
					8dcae973ef | ||
| 
						 | 
					4cf35f7294 | ||
| 
						 | 
					94c77d151b | ||
| 
						 | 
					7f600e2b4b | ||
| 
						 | 
					c809d0ba87 | ||
| 
						 | 
					50f038ec89 | ||
| 
						 | 
					9199a255a2 | ||
| 
						 | 
					d324537b47 | ||
| 
						 | 
					d0c05685f7 | ||
| 
						 | 
					1063c930b5 | ||
| 
						 | 
					79cbd44366 | ||
| 
						 | 
					7fdac1c5cb | ||
| 
						 | 
					0c0cf72ebb | ||
| 
						 | 
					8e2fe175ed | ||
| 
						 | 
					d1cff037c9 | ||
| 
						 | 
					fc88a2fafa | ||
| 
						 | 
					45fcceb056 | ||
| 
						 | 
					7043477038 | ||
| 
						 | 
					7dd685cf54 | ||
| 
						 | 
					5f5e4969c0 | ||
| 
						 | 
					8a53fd19e9 | ||
| 
						 | 
					baf4714c36 | ||
| 
						 | 
					7ba9ac7a5b | ||
| 
						 | 
					85b8f26e8e | ||
| 
						 | 
					594a0f1410 | ||
| 
						 | 
					d317d757d7 | ||
| 
						 | 
					fdf0ba6318 | ||
| 
						 | 
					15bf7de5fa | ||
| 
						 | 
					d3402b058e | ||
| 
						 | 
					e7dfdd4031 | ||
| 
						 | 
					b2dd7b6364 | ||
| 
						 | 
					9bd6d9abbf | ||
| 
						 | 
					cd14428fea | ||
| 
						 | 
					19d9f03c2b | ||
| 
						 | 
					0d57e72bbf | ||
| 
						 | 
					329516a61b | ||
| 
						 | 
					d566869589 | ||
| 
						 | 
					9cb8d8e6c7 | ||
| 
						 | 
					9de3c57e5d | ||
| 
						 | 
					f32ff92b0b | ||
| 
						 | 
					88d71e271e | ||
| 
						 | 
					fd9c14612a | ||
| 
						 | 
					e26e5a160f | ||
| 
						 | 
					b836bfed22 | ||
| 
						 | 
					a4b598c6d0 | ||
| 
						 | 
					c9ab755839 | ||
| 
						 | 
					9920edba53 | ||
| 
						 | 
					12bd7280d1 | ||
| 
						 | 
					d30ea7f63b | ||
| 
						 | 
					ebd3390db6 | ||
| 
						 | 
					9a374a9ebc | ||
| 
						 | 
					b1bc22cb08 | ||
| 
						 | 
					4930d53890 | ||
| 
						 | 
					c31327b5bc | ||
| 
						 | 
					3f2aa1f1e1 | ||
| 
						 | 
					6e78c00a96 | ||
| 
						 | 
					c27dde085e | ||
| 
						 | 
					d26cc308c0 | ||
| 
						 | 
					fb1efdf290 | ||
| 
						 | 
					3c99f2a472 | ||
| 
						 | 
					affe9a44e0 | ||
| 
						 | 
					43730fa519 | ||
| 
						 | 
					d39aa22b09 | ||
| 
						 | 
					e232a6b6ea | ||
| 
						 | 
					71ebb36fe9 | ||
| 
						 | 
					78a0b86327 | ||
| 
						 | 
					2636c16a97 | ||
| 
						 | 
					fd77c0242d | ||
| 
						 | 
					e74819a900 | ||
| 
						 | 
					9b7f696c9b | ||
| 
						 | 
					0230d614e7 | ||
| 
						 | 
					252d99ad78 | ||
| 
						 | 
					1ffc200350 | ||
| 
						 | 
					807d89b2b2 | ||
| 
						 | 
					4013afa1f1 | ||
| 
						 | 
					a580927ceb | ||
| 
						 | 
					bf2cf52034 | ||
| 
						 | 
					81bb8b7c31 | ||
| 
						 | 
					a825007fb5 | ||
| 
						 | 
					988124d96a | ||
| 
						 | 
					f0de815296 | ||
| 
						 | 
					0e2d58c887 | ||
| 
						 | 
					b155382626 | ||
| 
						 | 
					f362d740af | ||
| 
						 | 
					4a85e31a4f | ||
| 
						 | 
					302c270ad5 | ||
| 
						 | 
					3c1517d0f3 | ||
| 
						 | 
					f9fb222044 | ||
| 
						 | 
					e8edc02ba3 | ||
| 
						 | 
					95a44e3053 | ||
| 
						 | 
					74a9fe9a87 | ||
| 
						 | 
					4d03f9ea1a | ||
| 
						 | 
					67c96ca991 | ||
| 
						 | 
					88fb793c68 | ||
| 
						 | 
					d6d02d8cc5 | ||
| 
						 | 
					c5a3f8e2e3 | ||
| 
						 | 
					27e8653a1a | ||
| 
						 | 
					863beda82c | ||
| 
						 | 
					bac84c3ecd | ||
| 
						 | 
					2fca2ad9f8 | ||
| 
						 | 
					dd75286fe0 | ||
| 
						 | 
					7f91792cf1 | ||
| 
						 | 
					0e0ccad311 | ||
| 
						 | 
					0691f72e67 | ||
| 
						 | 
					7e38a51720 | ||
| 
						 | 
					34ca8243a3 | ||
| 
						 | 
					112fea7632 | ||
| 
						 | 
					378763e4ee | ||
| 
						 | 
					517bd0394d | ||
| 
						 | 
					70adb97fb5 | ||
| 
						 | 
					623d44cabe | ||
| 
						 | 
					0d479ca00b | ||
| 
						 | 
					8bc49ef437 | ||
| 
						 | 
					f83fcec786 | ||
| 
						 | 
					93690ce40d | ||
| 
						 | 
					f82c5f2f27 | ||
| 
						 | 
					a83c1c3899 | ||
| 
						 | 
					91d6aed109 | ||
| 
						 | 
					db8f8fe51d | ||
| 
						 | 
					4596004b17 | ||
| 
						 | 
					d5540906cb | ||
| 
						 | 
					90796a979d | ||
| 
						 | 
					2190a87772 | ||
| 
						 | 
					c5953b83f8 | ||
| 
						 | 
					24bc60abf0 | ||
| 
						 | 
					31eee6b009 | ||
| 
						 | 
					c5da565a8f | ||
| 
						 | 
					947cd712e1 | ||
| 
						 | 
					edc208f96b | ||
| 
						 | 
					1fb0296ee7 | ||
| 
						 | 
					6488d3df87 | ||
| 
						 | 
					56189d78e0 | ||
| 
						 | 
					bff18127b8 | ||
| 
						 | 
					363206e0ba | ||
| 
						 | 
					fd3e378501 | ||
| 
						 | 
					4ba2fe4c9d | ||
| 
						 | 
					2c499626ad | ||
| 
						 | 
					2b581a03c3 | ||
| 
						 | 
					450c15210a | ||
| 
						 | 
					65fed8cc93 | ||
| 
						 | 
					4b64771ea2 | ||
| 
						 | 
					f39977a6ff | ||
| 
						 | 
					933b535caa | ||
| 
						 | 
					8abc5d2f20 | ||
| 
						 | 
					d8783cd994 | ||
| 
						 | 
					d5d087feb5 | ||
| 
						 | 
					6ba3399df7 | ||
| 
						 | 
					65124b3aa8 | ||
| 
						 | 
					98597f4726 | ||
| 
						 | 
					e7981f0d8e | ||
| 
						 | 
					cf654427c3 | ||
| 
						 | 
					ff2f628282 | ||
| 
						 | 
					ae818ca265 | ||
| 
						 | 
					0f2aed458e | ||
| 
						 | 
					d486c44ff6 | ||
| 
						 | 
					ca7b9980bf | ||
| 
						 | 
					3c71e6a8e3 | ||
| 
						 | 
					542442864c | ||
| 
						 | 
					5edb64fa85 | ||
| 
						 | 
					8dc1c898a3 | ||
| 
						 | 
					1ed35726b0 | ||
| 
						 | 
					27fae9ebaa | ||
| 
						 | 
					b103f25c94 | ||
| 
						 | 
					abff450274 | ||
| 
						 | 
					c260736a11 | ||
| 
						 | 
					166ac2307a | ||
| 
						 | 
					b21a4e1a4d | ||
| 
						 | 
					f7dc943fa3 | ||
| 
						 | 
					bfbd2693ec | ||
| 
						 | 
					819e71c993 | ||
| 
						 | 
					9fd0b489a2 | ||
| 
						 | 
					f5fe9f8dae | ||
| 
						 | 
					f9ffc18145 | ||
| 
						 | 
					08db5b983a | ||
| 
						 | 
					5b3b4c8c50 | ||
| 
						 | 
					73f914ffc4 | ||
| 
						 | 
					d6bdd73ed6 | ||
| 
						 | 
					7370ee7349 | ||
| 
						 | 
					4574596bac | ||
| 
						 | 
					4d16855e36 | ||
| 
						 | 
					13a0d4d282 | ||
| 
						 | 
					b9cd06b829 | ||
| 
						 | 
					5b460e8fa2 | ||
| 
						 | 
					41087edf17 | ||
| 
						 | 
					2afcc38e38 | ||
| 
						 | 
					e59ccce25f | ||
| 
						 | 
					d7425890e8 | ||
| 
						 | 
					a989a837fb | ||
| 
						 | 
					db1221da50 | ||
| 
						 | 
					cf794569ed | ||
| 
						 | 
					51e5bbab0d | ||
| 
						 | 
					2c197ed2b2 | ||
| 
						 | 
					d8fc6665b3 | ||
| 
						 | 
					c671a79822 | ||
| 
						 | 
					9d93ce4c41 | ||
| 
						 | 
					a6d99fe227 | ||
| 
						 | 
					923b8bca31 | ||
| 
						 | 
					e2c30d1c88 | ||
| 
						 | 
					b6d9f2a04e | ||
| 
						 | 
					57306ea664 | ||
| 
						 | 
					cd7f3fd02f | ||
| 
						 | 
					0482e077a8 | ||
| 
						 | 
					5f986a45ca | ||
| 
						 | 
					ca7b49c0d5 | ||
| 
						 | 
					52dd555e6c | ||
| 
						 | 
					579b1a59f9 | ||
| 
						 | 
					5299c5c4be | ||
| 
						 | 
					f7756bccef | ||
| 
						 | 
					a6b874d160 | ||
| 
						 | 
					3e5fb3ddcf | ||
| 
						 | 
					5e6bcb12d3 | ||
| 
						 | 
					14303f1429 | ||
| 
						 | 
					96711ba022 | ||
| 
						 | 
					cbfc0fdbdc | ||
| 
						 | 
					6e81886c0e | ||
| 
						 | 
					2d976bc132 | ||
| 
						 | 
					57f6a476af | ||
| 
						 | 
					8491ed296e | ||
| 
						 | 
					cd1288afdc | ||
| 
						 | 
					ec6c830cb0 | ||
| 
						 | 
					2f86ccc4bf | ||
| 
						 | 
					8ca445aec0 | ||
| 
						 | 
					1e1f27c8a5 | ||
| 
						 | 
					2b84bde367 | ||
| 
						 | 
					b52e58551d | ||
| 
						 | 
					9aceed00bf | ||
| 
						 | 
					58814f7f74 | ||
| 
						 | 
					6a70ef9f31 | ||
| 
						 | 
					82cc4ca500 | ||
| 
						 | 
					4567fa04ed | ||
| 
						 | 
					8b98b5d818 | ||
| 
						 | 
					176d0351af | ||
| 
						 | 
					d63dc3384b | ||
| 
						 | 
					1ccd704e30 | ||
| 
						 | 
					f5d23dbe79 | ||
| 
						 | 
					75bfe53ac3 | ||
| 
						 | 
					3308f916dd | ||
| 
						 | 
					e7140279ca | ||
| 
						 | 
					1034719f5e | ||
| 
						 | 
					2c00043a7f | ||
| 
						 | 
					65c695d9ce | ||
| 
						 | 
					57253fe46a | ||
| 
						 | 
					4e5c443440 | ||
| 
						 | 
					0b3b73d8ec | ||
| 
						 | 
					921eabc134 | ||
| 
						 | 
					0faa428751 | ||
| 
						 | 
					f71a2fdd63 | ||
| 
						 | 
					4eb9ed8aba | ||
| 
						 | 
					d7b549abb8 | ||
| 
						 | 
					95d723c578 | ||
| 
						 | 
					2fcd853e86 | ||
| 
						 | 
					07eef7c812 | ||
| 
						 | 
					b01e0757fa | ||
| 
						 | 
					32844a20c6 | ||
| 
						 | 
					5b6532c601 | ||
| 
						 | 
					2c5b4b4027 | ||
| 
						 | 
					72d7ecf195 | ||
| 
						 | 
					2cfa6b4306 | ||
| 
						 | 
					6f6ffde0ab | ||
| 
						 | 
					1694739a16 | ||
| 
						 | 
					95d1e8bfca | ||
| 
						 | 
					60dec08e3c | ||
| 
						 | 
					a99d71be93 | ||
| 
						 | 
					f1331b6a0c | ||
| 
						 | 
					10d66b642b | ||
| 
						 | 
					cd2310e4a8 | ||
| 
						 | 
					1b399cf6b0 | ||
| 
						 | 
					877445bc0a | ||
| 
						 | 
					9a5b345bde | ||
| 
						 | 
					fc9e8ea7b3 | ||
| 
						 | 
					32be6fcfc1 | ||
| 
						 | 
					49847236c2 | ||
| 
						 | 
					d8424443e6 | ||
| 
						 | 
					f3b571ec3f | ||
| 
						 | 
					99318bb5d7 | ||
| 
						 | 
					1aa154c9aa | ||
| 
						 | 
					c65d8a445b | ||
| 
						 | 
					80f4f85570 | ||
| 
						 | 
					5beee43a6b | ||
| 
						 | 
					8d6ae203a0 | ||
| 
						 | 
					4353479a5c | ||
| 
						 | 
					34d7687f9e | ||
| 
						 | 
					b1dc3cf4af | ||
| 
						 | 
					6a58b95933 | ||
| 
						 | 
					d3badfd02b | ||
| 
						 | 
					0098be057b | ||
| 
						 | 
					6f972aa515 | ||
| 
						 | 
					7407ba6313 | ||
| 
						 | 
					1c79de207b | ||
| 
						 | 
					257c79db92 | ||
| 
						 | 
					9d1934a308 | ||
| 
						 | 
					d70f959902 | ||
| 
						 | 
					e4d810222f | ||
| 
						 | 
					bc1af4ae07 | ||
| 
						 | 
					6e688ef43f | ||
| 
						 | 
					f0fe1b23dc | ||
| 
						 | 
					aaf2006401 | ||
| 
						 | 
					b821e26935 | ||
| 
						 | 
					7ae4287157 | ||
| 
						 | 
					c6fcc38a65 | ||
| 
						 | 
					ab2d5c8853 | ||
| 
						 | 
					5e557ff0bc | ||
| 
						 | 
					918ca449a1 | ||
| 
						 | 
					8e73368008 | ||
| 
						 | 
					f3c1faf672 | ||
| 
						 | 
					d6df04dd6a | ||
| 
						 | 
					b1b9e51ab6 | ||
| 
						 | 
					e49d4770ac | ||
| 
						 | 
					8fa1075511 | ||
| 
						 | 
					9a70169b94 | ||
| 
						 | 
					fefb928237 | ||
| 
						 | 
					ad7e700d0d | ||
| 
						 | 
					1699c69147 | ||
| 
						 | 
					1695f7cece | ||
| 
						 | 
					052c27f907 | ||
| 
						 | 
					dc46c32b30 | ||
| 
						 | 
					fa63349bb2 | ||
| 
						 | 
					ffe26448a6 | ||
| 
						 | 
					4af51e8a84 | ||
| 
						 | 
					1e453cf5a5 | ||
| 
						 | 
					591282b87d | ||
| 
						 | 
					e87528d520 | ||
| 
						 | 
					d79eb0411d | ||
| 
						 | 
					ac1e0a4cf7 | ||
| 
						 | 
					9525eab130 | ||
| 
						 | 
					89b317496c | ||
| 
						 | 
					13be91e78b | ||
| 
						 | 
					f68c1437f3 | ||
| 
						 | 
					4c64c969bb | ||
| 
						 | 
					b4bf3b5138 | ||
| 
						 | 
					083bc4b400 | ||
| 
						 | 
					e8683c5bcc | ||
| 
						 | 
					80e0d1de91 | ||
| 
						 | 
					dbe841037e | ||
| 
						 | 
					bdd537c33c | ||
| 
						 | 
					c0c3846094 | ||
| 
						 | 
					9e8710e7d2 | ||
| 
						 | 
					475553fdf6 | ||
| 
						 | 
					9d570f5b45 | ||
| 
						 | 
					af7fafd34f | ||
| 
						 | 
					d43130f4fc | ||
| 
						 | 
					7500194620 | ||
| 
						 | 
					eb27c29144 | ||
| 
						 | 
					43260b3e24 | ||
| 
						 | 
					f80713f0aa | ||
| 
						 | 
					0c4bdc7ad1 | ||
| 
						 | 
					811cff7bd0 | ||
| 
						 | 
					30269aa75c | ||
| 
						 | 
					e345ef7083 | ||
| 
						 | 
					f559c9b8f7 | ||
| 
						 | 
					f4af0916b2 | ||
| 
						 | 
					f15f14f28d | ||
| 
						 | 
					834f44f58d | ||
| 
						 | 
					b36f45dcf4 | ||
| 
						 | 
					11ba21c9a8 | ||
| 
						 | 
					b045557ce1 | ||
| 
						 | 
					0dd251a3f6 | ||
| 
						 | 
					793acb1725 | ||
| 
						 | 
					921243e8bd | ||
| 
						 | 
					bd9d7a90d9 | ||
| 
						 | 
					cc444a4cea | ||
| 
						 | 
					38ca1fa168 | ||
| 
						 | 
					7a552b87ec | ||
| 
						 | 
					36923d3190 | ||
| 
						 | 
					a9d3017123 | ||
| 
						 | 
					313acd4976 | ||
| 
						 | 
					a4c91bb268 | ||
| 
						 | 
					f9b566984b | ||
| 
						 | 
					8dd261854d | ||
| 
						 | 
					7351e62d87 | ||
| 
						 | 
					0593ae720b | ||
| 
						 | 
					a0a7b08e08 | ||
| 
						 | 
					9a3bc6b8b3 | ||
| 
						 | 
					5acae17f71 | ||
| 
						 | 
					f1e5b76ef2 | ||
| 
						 | 
					53c628fde9 | ||
| 
						 | 
					baca0a70c0 | ||
| 
						 | 
					3e8d0af404 | ||
| 
						 | 
					cf9a91d9d5 | ||
| 
						 | 
					02b9e282c6 | ||
| 
						 | 
					9ce87f235f | ||
| 
						 | 
					e329bea1b2 | ||
| 
						 | 
					8086e7b54d | ||
| 
						 | 
					f7a875606e | ||
| 
						 | 
					196eaf85f4 | ||
| 
						 | 
					876a55668e | ||
| 
						 | 
					05bd21bdd5 | ||
| 
						 | 
					fb51a08cc6 | ||
| 
						 | 
					dd83d7f4d3 | ||
| 
						 | 
					842a56f7ce | ||
| 
						 | 
					9246a6e797 | ||
| 
						 | 
					8ad693f717 | ||
| 
						 | 
					f4c2ee7cc4 | ||
| 
						 | 
					6043441faa | ||
| 
						 | 
					4a065c3710 | ||
| 
						 | 
					0ef800bdd7 | ||
| 
						 | 
					56eaa1910d | ||
| 
						 | 
					201788e286 | ||
| 
						 | 
					506e0f144f | ||
| 
						 | 
					72f68bfdd9 | ||
| 
						 | 
					2f9869b11d | ||
| 
						 | 
					8ffcf6498c | ||
| 
						 | 
					d224ae1923 | ||
| 
						 | 
					fed2063a19 | ||
| 
						 | 
					db2810cdd7 | ||
| 
						 | 
					4f1a6781ef | ||
| 
						 | 
					beffa5d5a4 | ||
| 
						 | 
					7a20f1de07 | ||
| 
						 | 
					cd25cf726b | ||
| 
						 | 
					d6b1bc3842 | ||
| 
						 | 
					a4385fb9bb | ||
| 
						 | 
					7045f2b8ea | ||
| 
						 | 
					07ca1a4de8 | ||
| 
						 | 
					24f289e692 | ||
| 
						 | 
					01bcdaae2d | ||
| 
						 | 
					55890008d1 | ||
| 
						 | 
					5ab9b01879 | ||
| 
						 | 
					e4abb333b3 | ||
| 
						 | 
					09f476c745 | ||
| 
						 | 
					8806e68dce | ||
| 
						 | 
					2ef1e25cd8 | ||
| 
						 | 
					10e7f202aa | ||
| 
						 | 
					ccd7000c09 | ||
| 
						 | 
					8ee7b798cf | ||
| 
						 | 
					7733cf5bf0 | ||
| 
						 | 
					a05ce86dd7 | ||
| 
						 | 
					91f51c32e8 | ||
| 
						 | 
					f910202bba | ||
| 
						 | 
					6d77194a8f | ||
| 
						 | 
					9deb89c15f | ||
| 
						 | 
					4b62a092b4 | ||
| 
						 | 
					81c8f626f9 | ||
| 
						 | 
					3e846c42fb | ||
| 
						 | 
					63ad7fd766 | ||
| 
						 | 
					9ff1e9aa34 | ||
| 
						 | 
					8d162b6f3d | ||
| 
						 | 
					9844d10bef | ||
| 
						 | 
					b908fa8489 | ||
| 
						 | 
					15a10643a7 | ||
| 
						 | 
					299617aca1 | ||
| 
						 | 
					45647d697a | ||
| 
						 | 
					48f5105d38 | ||
| 
						 | 
					fe1c741d68 | ||
| 
						 | 
					fa42cc1f00 | ||
| 
						 | 
					42cf5e7a81 | ||
| 
						 | 
					47905e1aa1 | ||
| 
						 | 
					9a8e907df3 | ||
| 
						 | 
					106fe85582 | ||
| 
						 | 
					4b3571bd57 | ||
| 
						 | 
					96b537401a | ||
| 
						 | 
					721c9eb057 | ||
| 
						 | 
					51701bf6d6 | ||
| 
						 | 
					dbde68bd56 | ||
| 
						 | 
					ad2c9f585a | ||
| 
						 | 
					562093c468 | ||
| 
						 | 
					b0295584a3 | ||
| 
						 | 
					208c54de98 | ||
| 
						 | 
					63e2d941a1 | ||
| 
						 | 
					3956838e9c | ||
| 
						 | 
					abeee58bb0 | ||
| 
						 | 
					d5b1b49722 | ||
| 
						 | 
					564ed03ff8 | ||
| 
						 | 
					70db4c76b4 | ||
| 
						 | 
					d059f7975b | ||
| 
						 | 
					4e74e6dc2d | ||
| 
						 | 
					b6deb96658 | ||
| 
						 | 
					3839e966be | ||
| 
						 | 
					3dd035849c | ||
| 
						 | 
					3d6532b5d6 | ||
| 
						 | 
					bf7c175ee7 | ||
| 
						 | 
					f84af35ed6 | ||
| 
						 | 
					99063b3eb1 | ||
| 
						 | 
					3bec18f28d | ||
| 
						 | 
					15de7a7894 | ||
| 
						 | 
					e20e04e677 | ||
| 
						 | 
					5fc6ae2835 | ||
| 
						 | 
					7d281b8c96 | ||
| 
						 | 
					4880b801a7 | ||
| 
						 | 
					74e354456a | ||
| 
						 | 
					af2e03aa36 | ||
| 
						 | 
					d8fa660ab6 | ||
| 
						 | 
					1a62d48297 | ||
| 
						 | 
					7ba01be13d | ||
| 
						 | 
					1a83d64db7 | ||
| 
						 | 
					5b53014c40 | ||
| 
						 | 
					83685340af | ||
| 
						 | 
					31e0cc4dec | ||
| 
						 | 
					56b87fc1f5 | ||
| 
						 | 
					6b956a2dd7 | ||
| 
						 | 
					1937623d7d | ||
| 
						 | 
					3b60b10945 | ||
| 
						 | 
					7173acd350 | ||
| 
						 | 
					6310d87338 | ||
| 
						 | 
					49a1ed7c18 | ||
| 
						 | 
					d426e280d9 | ||
| 
						 | 
					6154fb29f1 | ||
| 
						 | 
					97d48ef9d6 | ||
| 
						 | 
					88992625c4 | ||
| 
						 | 
					bc6eb44218 | ||
| 
						 | 
					cf9ccd799d | ||
| 
						 | 
					ffa0e4e771 | ||
| 
						 | 
					60fa9c196c | ||
| 
						 | 
					df860d22fb | ||
| 
						 | 
					cb46ff326c | ||
| 
						 | 
					f277a853ef | ||
| 
						 | 
					9ae34f67c3 | ||
| 
						 | 
					c9223218cc | ||
| 
						 | 
					c0dd645aba | ||
| 
						 | 
					2e948eb5b6 | ||
| 
						 | 
					c3276889cf | ||
| 
						 | 
					a76ca8282d | ||
| 
						 | 
					8ce6b8362f | ||
| 
						 | 
					842fb12f05 | ||
| 
						 | 
					d63e1511af | ||
| 
						 | 
					278783b8e0 | ||
| 
						 | 
					d24e3c922d | ||
| 
						 | 
					1d02cd2283 | ||
| 
						 | 
					8edeb82a87 | ||
| 
						 | 
					146e9279de | ||
| 
						 | 
					47105f50a9 | ||
| 
						 | 
					16c9c80f37 | ||
| 
						 | 
					8e7e4bc95a | ||
| 
						 | 
					0aa3d2f930 | ||
| 
						 | 
					ce77755a1e | ||
| 
						 | 
					0f31f20c87 | ||
| 
						 | 
					ee6da2aaa5 | ||
| 
						 | 
					a35f087cd9 | ||
| 
						 | 
					6e029b44dd | ||
| 
						 | 
					973c0cff34 | ||
| 
						 | 
					2027eea6ac | ||
| 
						 | 
					2f43692f33 | ||
| 
						 | 
					6d24992f88 | ||
| 
						 | 
					b4388a58d6 | ||
| 
						 | 
					158aa05fac | ||
| 
						 | 
					f2731bf55e | ||
| 
						 | 
					7304e99fce | ||
| 
						 | 
					02700b83eb | ||
| 
						 | 
					676b25acf9 | ||
| 
						 | 
					556359ea2d | ||
| 
						 | 
					b72923e0f5 | ||
| 
						 | 
					115ac9f75e | ||
| 
						 | 
					32e36f6708 | ||
| 
						 | 
					d949b7a4f9 | ||
| 
						 | 
					eae1171ff5 | ||
| 
						 | 
					76a1b75a51 | ||
| 
						 | 
					8882c0daea | ||
| 
						 | 
					07ebc16d59 | ||
| 
						 | 
					0ceb109964 | ||
| 
						 | 
					118b0d0038 | ||
| 
						 | 
					5e87067792 | ||
| 
						 | 
					c946a252e8 | ||
| 
						 | 
					f9ad2ba1dd | ||
| 
						 | 
					0d0ecd33bd | ||
| 
						 | 
					e4b98fd05b | ||
| 
						 | 
					95a5933303 | ||
| 
						 | 
					da3b55fa64 | ||
| 
						 | 
					fbbabfb90e | ||
| 
						 | 
					f13da6830d | ||
| 
						 | 
					f560a8e2f8 | ||
| 
						 | 
					56f1139c2f | ||
| 
						 | 
					773bdfc1e2 | ||
| 
						 | 
					f449666628 | ||
| 
						 | 
					3f282de0ab | ||
| 
						 | 
					440dd8d22f | ||
| 
						 | 
					dcff9de2f7 | ||
| 
						 | 
					a192866543 | ||
| 
						 | 
					10081416de | ||
| 
						 | 
					e2bed618f9 | ||
| 
						 | 
					03ab1f3823 | ||
| 
						 | 
					ac8aeb63d9 | ||
| 
						 | 
					2e16d822fa | ||
| 
						 | 
					e407d873fa | ||
| 
						 | 
					fd712a1dbe | ||
| 
						 | 
					e9028b40ce | ||
| 
						 | 
					c9da3dee7c | ||
| 
						 | 
					c8c224e202 | ||
| 
						 | 
					f34559daaf | ||
| 
						 | 
					9fefbf4c27 | ||
| 
						 | 
					1af9fd73ea | ||
| 
						 | 
					75ef394eff | ||
| 
						 | 
					ec6cc2c63e | ||
| 
						 | 
					06bc2e192b | ||
| 
						 | 
					78701ec7c1 | ||
| 
						 | 
					c925fab7e4 | ||
| 
						 | 
					42fd72c164 | ||
| 
						 | 
					7fd160e1a2 | ||
| 
						 | 
					97a0d940eb | ||
| 
						 | 
					efaa099d81 | ||
| 
						 | 
					47864a804b | ||
| 
						 | 
					91136c0e43 | ||
| 
						 | 
					28c3b1bd61 | ||
| 
						 | 
					551352bc40 | ||
| 
						 | 
					e73c24c925 | ||
| 
						 | 
					7ec4c286cc | ||
| 
						 | 
					6705e2ec4b | ||
| 
						 | 
					6f0373063b | ||
| 
						 | 
					f64eef60b5 | ||
| 
						 | 
					89546bf86b | ||
| 
						 | 
					793678feca | ||
| 
						 | 
					923cc3019a | ||
| 
						 | 
					10eb98a5f6 | ||
| 
						 | 
					bd9e89d8dd | ||
| 
						 | 
					1926b4ce73 | ||
| 
						 | 
					4ef3062d74 | ||
| 
						 | 
					abb6e0f60f | ||
| 
						 | 
					f204d8d84e | ||
| 
						 | 
					fa301656f1 | ||
| 
						 | 
					7e1221028f | ||
| 
						 | 
					41308cb2dd | ||
| 
						 | 
					130600521c | ||
| 
						 | 
					cd57548a48 | ||
| 
						 | 
					efacc99f76 | ||
| 
						 | 
					f0d236e172 | ||
| 
						 | 
					a8118bd8c6 | ||
| 
						 | 
					0e58f2ef53 | ||
| 
						 | 
					f4b22b3a0c | ||
| 
						 | 
					df5bd281c7 | ||
| 
						 | 
					a3f23837ce | ||
| 
						 | 
					612d989b97 | ||
| 
						 | 
					42c01ee9a2 | ||
| 
						 | 
					14074db591 | ||
| 
						 | 
					43dfdd7942 | ||
| 
						 | 
					f397b97ccf | ||
| 
						 | 
					95f8716144 | ||
| 
						 | 
					17ba472b2e | ||
| 
						 | 
					42d82571ab | ||
| 
						 | 
					9119a28141 | ||
| 
						 | 
					a32263d838 | ||
| 
						 | 
					208ae2bb88 | ||
| 
						 | 
					4d85462a85 | ||
| 
						 | 
					f601aa9ca0 | ||
| 
						 | 
					8aee3ad455 | ||
| 
						 | 
					6a2a1e9561 | ||
| 
						 | 
					5f8786c9dc | ||
| 
						 | 
					73f1d3eead | ||
| 
						 | 
					2bf21bb3c3 | ||
| 
						 | 
					f80f0dbb11 | ||
| 
						 | 
					37518c70c4 | ||
| 
						 | 
					e5951b5bef | ||
| 
						 | 
					ab320bd90b | ||
| 
						 | 
					7bd36b5371 | ||
| 
						 | 
					b882b0f2bc | ||
| 
						 | 
					38d7ae73cc | ||
| 
						 | 
					4527c6ee5d | ||
| 
						 | 
					85829e70c1 | ||
| 
						 | 
					256c08d82a | ||
| 
						 | 
					c2ce03c047 | ||
| 
						 | 
					f2af19e198 | ||
| 
						 | 
					930b7c092d | ||
| 
						 | 
					00757c69c6 | ||
| 
						 | 
					55f267d0fc | ||
| 
						 | 
					6b96aff6e8 | ||
| 
						 | 
					32b773a8fa | ||
| 
						 | 
					03089adad6 | ||
| 
						 | 
					4a1fe746ab | ||
| 
						 | 
					aa52c05d2c | ||
| 
						 | 
					26407a43e7 | ||
| 
						 | 
					a02934bf19 | ||
| 
						 | 
					09c65fba09 | ||
| 
						 | 
					4305c727d0 | ||
| 
						 | 
					188339897f | ||
| 
						 | 
					4ecff9a707 | ||
| 
						 | 
					355aed49c6 | ||
| 
						 | 
					4717b6b0f0 | ||
| 
						 | 
					45ebe9048d | ||
| 
						 | 
					b2170c49a3 | ||
| 
						 | 
					dc2f4d6115 | ||
| 
						 | 
					1eb132440f | ||
| 
						 | 
					a464bbc37a | ||
| 
						 | 
					ed995697c2 | ||
| 
						 | 
					163cd84c7b | ||
| 
						 | 
					293d7cc292 | ||
| 
						 | 
					5de1b4e74c | ||
| 
						 | 
					7b474975da | ||
| 
						 | 
					beab51516b | ||
| 
						 | 
					fe8685a50c | ||
| 
						 | 
					f9af5d0885 | ||
| 
						 | 
					e8136a9720 | ||
| 
						 | 
					531e5d4556 | ||
| 
						 | 
					e66255963a | ||
| 
						 | 
					246aac8ee4 | ||
| 
						 | 
					23cfeff685 | ||
| 
						 | 
					a5e7e0d126 | ||
| 
						 | 
					5bebc30ba0 | ||
| 
						 | 
					0e7057f5b9 | ||
| 
						 | 
					7c6c365ba4 | ||
| 
						 | 
					424c9bb0c5 | ||
| 
						 | 
					9d0f26594c | ||
| 
						 | 
					99c17de079 | ||
| 
						 | 
					b1e3dd0af6 | ||
| 
						 | 
					261cb89530 | ||
| 
						 | 
					ff6773ba37 | ||
| 
						 | 
					bdfbbfcbbd | ||
| 
						 | 
					0c4cd56758 | ||
| 
						 | 
					4a36658321 | ||
| 
						 | 
					7aae938685 | ||
| 
						 | 
					3723401e7a | ||
| 
						 | 
					70631366a9 | ||
| 
						 | 
					0e40bbda3e | ||
| 
						 | 
					e9aa475398 | ||
| 
						 | 
					8d2a811184 | ||
| 
						 | 
					dd7f5b6700 | ||
| 
						 | 
					a4f6277737 | ||
| 
						 | 
					c2bfaacbb7 | ||
| 
						 | 
					a17cbfa2d4 | ||
| 
						 | 
					fb9a101555 | ||
| 
						 | 
					e319cf0200 | ||
| 
						 | 
					0a8395ef6a | ||
| 
						 | 
					38df5e01be | ||
| 
						 | 
					ebd891a868 | ||
| 
						 | 
					4ab2395cbe | ||
| 
						 | 
					5f1f989fc9 | ||
| 
						 | 
					44b709eee3 | ||
| 
						 | 
					d0d7726597 | ||
| 
						 | 
					054c342aeb | ||
| 
						 | 
					c79c33baf7 | ||
| 
						 | 
					23b00e35b2 | ||
| 
						 | 
					fe51079266 | ||
| 
						 | 
					0791b0bbee | ||
| 
						 | 
					dbf04c8eeb | ||
| 
						 | 
					6204256df8 | ||
| 
						 | 
					93cc8c2327 | ||
| 
						 | 
					68a2e5bbbc | ||
| 
						 | 
					72792153f2 | ||
| 
						 | 
					88b6ef1897 | 
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -362,8 +362,7 @@ MigrationBackup/
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
/src/ThingsGateway.Web.Server/Default.db
 | 
			
		||||
/src/PluginPro*/
 | 
			
		||||
/src/*Pro*
 | 
			
		||||
/src/TestResults*/
 | 
			
		||||
/src/ThingsGateway.Web.Server/ThingsGateway.db
 | 
			
		||||
 | 
			
		||||
/framework/*pro*
 | 
			
		||||
/framework/*Pro*
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Image/1.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 135 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/2.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 125 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/3.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 217 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/4.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 119 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/5.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 189 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/6.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 151 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/7.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 187 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/8.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 164 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Image/9.png
									
									
									
									
									
								
							
							
						
						| 
		 Before Width: | Height: | Size: 149 KiB  | 
							
								
								
									
										21
									
								
								LICENSE.txt
									
									
									
									
									
								
							
							
						
						@@ -1,3 +1,4 @@
 | 
			
		||||
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
@@ -186,16 +187,16 @@
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   Copyright [yyyy] [name of copyright owner]
 | 
			
		||||
   Copyright 2023-present Diego
 | 
			
		||||
 | 
			
		||||
   Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
   you may not use this file except in compliance with the License.
 | 
			
		||||
   You may obtain a copy of the License at
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
       http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
   Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
   distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
   See the License for the specific language governing permissions and
 | 
			
		||||
   limitations under the License.
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
							
								
								
									
										84
									
								
								README.md
									
									
									
									
									
								
							
							
						
						@@ -1,78 +1,38 @@
 | 
			
		||||
<div align="center"><h1 align="center">ThingsGateway</a></h1></div>
 | 
			
		||||
<div align="center"><h3 align="center">边缘采集网关</h3></div>
 | 
			
		||||
 | 
			
		||||
#### 介绍
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
基于[ThingsBlazor](https://gitee.com/diego2098/ThingsBlazor)权限管理框架开发的跨平台边缘采集网关,支持南北端插件式开发,
 | 
			
		||||
动态更新插件,
 | 
			
		||||
并拥有较完善的北端Rpc权限管理。
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
 **NetCore** 跨平台边缘采集网关(工业设备采集)
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 **ThingsGateway - Admin**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####  功能亮点
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
- Blazor Server架构,开发部署更简单
 | 
			
		||||
- 采集/上传配置完全支持Excel导入导出
 | 
			
		||||
- 插件式驱动,方便驱动二次开发,并支持动态更新
 | 
			
		||||
- Modbus、OPCDA、OPCUA采集插件(支持导入节点)
 | 
			
		||||
- Modbus Server、Mqtt Server(Client)、OPC UAServer插件,支持Rpc写入,支持WebApi
 | 
			
		||||
- 支持时序数据库存储
 | 
			
		||||
- 实时/历史报警(Sql转储),支持布尔/高低限值
 | 
			
		||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
 | 
			
		||||
 | 
			
		||||
#### nuget
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
- Modbus库,支持ModbusTcp、ModbusRtuOverTcp、ModbusUdp、ModbusServer等
 | 
			
		||||
``` powershell
 | 
			
		||||
 dotnet add package ThingsGateway.Foundation.Adapter.Modbus
 | 
			
		||||
```
 | 
			
		||||
- OPCDA客户端库,支持X64,支持NetCore,支持检测重连
 | 
			
		||||
``` powershell
 | 
			
		||||
 dotnet add package ThingsGateway.Foundation.Adapter.OPCDA
 | 
			
		||||
```
 | 
			
		||||
- OPCUA客户端库
 | 
			
		||||
``` powershell
 | 
			
		||||
 dotnet add package ThingsGateway.Foundation.Adapter.OPCUA
 | 
			
		||||
```
 | 
			
		||||
####  效果图
 | 
			
		||||
 <table>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/1.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/2.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/3.png"/></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
    <tr>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/4.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/5.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/6.png"/></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/7.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/8.png"/></td>
 | 
			
		||||
        <td><img src="https://gitee.com/diego2098/ThingsGateway/raw/master/Image/9.png"/></td>
 | 
			
		||||
    </tr>
 | 
			
		||||
 </table>
 | 
			
		||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
 | 
			
		||||
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
 ####  文档
 | 
			
		||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
 | 
			
		||||
 | 
			
		||||
 使用前请查看Gitee Pages [文档站点](https://diego2098.gitee.io/thingsgateway/)
 | 
			
		||||
账户	:  **superAdmin**	
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
#### 补充说明
 | 
			
		||||
* 使用OPC相关插件时请遵循OPC基金会的授权规则
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
 | 
			
		||||
#### 开源协议
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
请仔细阅读 [授权协议](https://diego2098.gitee.io/thingsgateway/docs/)
 | 
			
		||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
####  支持作者
 | 
			
		||||
 如果对您有帮助,请点击右上角⭐Star关注,感谢支持开源!
 | 
			
		||||
 若希望捐赠项目,请跳转到文档站点查看捐赠码或使用Gitee捐赠功能
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
####  联系作者
 | 
			
		||||
 * QQ群:605534569
 | 
			
		||||
 * 邮箱:2248356998@qq.com
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										101
									
								
								framework/ThingsGateway - Admin.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,101 @@
 | 
			
		||||
 | 
			
		||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
			
		||||
# Visual Studio Version 17
 | 
			
		||||
VisualStudioVersion = 17.6.33927.249
 | 
			
		||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		admin\Directory.Build.props = admin\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		web\Directory.Build.props = web\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		..\.gitignore = ..\.gitignore
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj", "{6961511A-8787-42AF-827D-B630B2AF4791}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{268A1A81-2685-47E1-9986-5934A58A31A4}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
		Release|Any CPU = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{6961511A-8787-42AF-827D-B630B2AF4791}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6961511A-8787-42AF-827D-B630B2AF4791}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(NestedProjects) = preSolution
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
 | 
			
		||||
		{6961511A-8787-42AF-827D-B630B2AF4791} = {268A1A81-2685-47E1-9986-5934A58A31A4}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
							
								
								
									
										288
									
								
								framework/ThingsGateway.sln
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,288 @@
 | 
			
		||||
 | 
			
		||||
Microsoft Visual Studio Solution File, Format Version 12.00
 | 
			
		||||
# Visual Studio Version 17
 | 
			
		||||
VisualStudioVersion = 17.6.33927.249
 | 
			
		||||
MinimumVisualStudioVersion = 10.0.40219.1
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation", "foundation\ThingsGateway.foundation\ThingsGateway.Foundation.csproj", "{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "foundation", "foundation", "{0874CBC5-C583-4FAD-BA93-94571D446898}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		foundation\Directory.Build.props = foundation\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.DLT645", "foundation\ThingsGateway.Foundation.Adapter.DLT645\ThingsGateway.Foundation.Adapter.DLT645.csproj", "{92963877-0185-45A5-A0EB-CEC0D55FF9B0}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Modbus", "foundation\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj", "{24510CCE-0B60-4A03-9719-CF367C3528E9}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCDA", "foundation\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj", "{566783A4-222B-46F5-AA12-0753997B3254}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.OPCUA", "foundation\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj", "{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Siemens", "foundation\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj", "{9695B353-D773-40DD-B65E-7B10EB0C16EC}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "demo", "demo", "{358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		demo\Directory.Build.props = demo\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{2192217C-CF77-422E-9E63-DF4003ABF01A}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Rcl", "demo\ThingsGateway.Foundation.Demo.Rcl\ThingsGateway.Foundation.Demo.Rcl.csproj", "{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Winform", "demo\ThingsGateway.Foundation.Demo.Winform\ThingsGateway.Foundation.Demo.Winform.csproj", "{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gateway", "gateway", "{DFB97103-FC3A-4DC3-A327-DA8C65BDA607}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		gateway\Directory.Build.props = gateway\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{4E66C22C-0636-4949-BF6A-9E3BBE1550BA}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		admin\Directory.Build.props = admin\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Components", "admin\ThingsGateway.Components\ThingsGateway.Components.csproj", "{0A891D8E-23B3-46AD-8D30-565EE5004F93}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "admin\ThingsGateway.Core\ThingsGateway.Core.csproj", "{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Core", "admin\ThingsGateway.Admin.Core\ThingsGateway.Admin.Core.csproj", "{5DA3D2BD-6768-4479-B52F-49E022EFF310}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Blazor", "admin\ThingsGateway.Admin.Blazor\ThingsGateway.Admin.Blazor.csproj", "{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.Application", "admin\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj", "{D6685A42-2712-417A-92C5-5EFF90B9FA94}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Admin.ApiController", "admin\ThingsGateway.Admin.ApiController\ThingsGateway.Admin.ApiController.csproj", "{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "gateway\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazor", "gateway\ThingsGateway.Gateway.Blazor\ThingsGateway.Gateway.Blazor.csproj", "{69EA3D89-CB40-425A-8D70-5E4A33337BE5}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{90AB5C24-1AA3-4F58-9987-B307B92B5193}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "gateway\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{4C7A5A90-8292-413B-8848-419D9DCDE66F}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		web\Directory.Build.props = web\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "web\ThingsGateway.Web.Core\ThingsGateway.Web.Core.csproj", "{D37EC028-EA46-4510-8261-6E780A906314}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Entry", "web\ThingsGateway.Web.Entry\ThingsGateway.Web.Entry.csproj", "{C5F662EB-991F-438D-BF61-EF87E7371C04}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugin", "plugin", "{E65490B8-D2E2-4693-B39C-15703B1EBFBB}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		plugin\Directory.Build.props = plugin\Directory.Build.props
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Modbus", "plugin\ThingsGateway.Plugin.Modbus\ThingsGateway.Plugin.Modbus.csproj", "{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Mqtt", "plugin\ThingsGateway.Plugin.Mqtt\ThingsGateway.Plugin.Mqtt.csproj", "{53C870AE-0249-4485-AE63-F8A16EA4BCB4}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "解决方案项", "解决方案项", "{97B23D8B-C6C0-4746-A21F-C7B49354B284}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		..\.gitignore = ..\.gitignore
 | 
			
		||||
		..\README.md = ..\README.md
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Kafka", "plugin\ThingsGateway.Plugin.Kafka\ThingsGateway.Plugin.Kafka.csproj", "{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{C102AB3A-377E-4753-AFA7-C13250D7DF00}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{0DE1D254-0BD2-4D98-B939-5440BE5EB552}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.RabbitMQ", "plugin\ThingsGateway.Plugin.RabbitMQ\ThingsGateway.Plugin.RabbitMQ.csproj", "{A8E68E17-4EBF-4E4C-8272-B489329A68BF}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.Siemens", "plugin\ThingsGateway.Plugin.Siemens\ThingsGateway.Plugin.Siemens.csproj", "{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.DLT645", "plugin\ThingsGateway.Plugin.DLT645\ThingsGateway.Plugin.DLT645.csproj", "{AC4295F2-AB2F-4137-99EF-80FA5C83896B}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCDA", "plugin\ThingsGateway.Plugin.OPCDA\ThingsGateway.Plugin.OPCDA.csproj", "{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.OPCUA", "plugin\ThingsGateway.Plugin.OPCUA\ThingsGateway.Plugin.OPCUA.csproj", "{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLHisAlarm", "plugin\ThingsGateway.Plugin.SQLHisAlarm\ThingsGateway.Plugin.SQLHisAlarm.csproj", "{0947E6C0-6371-4483-B0ED-191FB5073151}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
		Release|Any CPU = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
 | 
			
		||||
		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{92963877-0185-45A5-A0EB-CEC0D55FF9B0}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{24510CCE-0B60-4A03-9719-CF367C3528E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{24510CCE-0B60-4A03-9719-CF367C3528E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{24510CCE-0B60-4A03-9719-CF367C3528E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{24510CCE-0B60-4A03-9719-CF367C3528E9}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{566783A4-222B-46F5-AA12-0753997B3254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{566783A4-222B-46F5-AA12-0753997B3254}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{566783A4-222B-46F5-AA12-0753997B3254}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{566783A4-222B-46F5-AA12-0753997B3254}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{9695B353-D773-40DD-B65E-7B10EB0C16EC}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{2192217C-CF77-422E-9E63-DF4003ABF01A}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{69EA3D89-CB40-425A-8D70-5E4A33337BE5}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{90AB5C24-1AA3-4F58-9987-B307B92B5193}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{4C7A5A90-8292-413B-8848-419D9DCDE66F}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{53C870AE-0249-4485-AE63-F8A16EA4BCB4}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{C102AB3A-377E-4753-AFA7-C13250D7DF00}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0DE1D254-0BD2-4D98-B939-5440BE5EB552}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{A8E68E17-4EBF-4E4C-8272-B489329A68BF}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{AC4295F2-AB2F-4137-99EF-80FA5C83896B}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{0947E6C0-6371-4483-B0ED-191FB5073151}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{0947E6C0-6371-4483-B0ED-191FB5073151}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{0947E6C0-6371-4483-B0ED-191FB5073151}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(NestedProjects) = preSolution
 | 
			
		||||
		{2070116F-7A1B-47E2-A2F2-7BEC29ECE891} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{92963877-0185-45A5-A0EB-CEC0D55FF9B0} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{24510CCE-0B60-4A03-9719-CF367C3528E9} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{566783A4-222B-46F5-AA12-0753997B3254} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{9695B353-D773-40DD-B65E-7B10EB0C16EC} = {0874CBC5-C583-4FAD-BA93-94571D446898}
 | 
			
		||||
		{2192217C-CF77-422E-9E63-DF4003ABF01A} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
 | 
			
		||||
		{DE376FAD-1E99-4CEE-96B2-08B1D1F3D1DB} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
 | 
			
		||||
		{ACAA3F0D-CE2B-49F4-883A-ED23ADD9E325} = {358AE1E1-4DD6-4D1F-8935-1ACBB0111A01}
 | 
			
		||||
		{0A891D8E-23B3-46AD-8D30-565EE5004F93} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{A712EAEE-94F2-4F01-8C1C-2EC802280DD7} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{5DA3D2BD-6768-4479-B52F-49E022EFF310} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{8DD5DF98-7FDE-4B49-8661-AEB44D923CFE} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{D6685A42-2712-417A-92C5-5EFF90B9FA94} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{0D17D801-6DAA-4FD1-9A99-F9F07FA6BA88} = {4E66C22C-0636-4949-BF6A-9E3BBE1550BA}
 | 
			
		||||
		{1A4D0B95-9FC5-4687-94E2-B6F86B5427F2} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
 | 
			
		||||
		{69EA3D89-CB40-425A-8D70-5E4A33337BE5} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
 | 
			
		||||
		{90AB5C24-1AA3-4F58-9987-B307B92B5193} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
 | 
			
		||||
		{4C7A5A90-8292-413B-8848-419D9DCDE66F} = {DFB97103-FC3A-4DC3-A327-DA8C65BDA607}
 | 
			
		||||
		{D37EC028-EA46-4510-8261-6E780A906314} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
 | 
			
		||||
		{C5F662EB-991F-438D-BF61-EF87E7371C04} = {F0C9A8CB-231B-45E0-B91B-4FEF7EF47197}
 | 
			
		||||
		{ECF3A7A3-2363-4A38-BC78-8FF9A8564603} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{53C870AE-0249-4485-AE63-F8A16EA4BCB4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{E4B8B8E6-FAE7-43BA-9A51-33A3CD9FB825} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{C102AB3A-377E-4753-AFA7-C13250D7DF00} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{0DE1D254-0BD2-4D98-B939-5440BE5EB552} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{C1911F84-6FDB-4DEC-8938-69B2E3901CC4} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{A8E68E17-4EBF-4E4C-8272-B489329A68BF} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{5FAFD46E-6B98-4C75-B1EB-085AA18F15FD} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{AC4295F2-AB2F-4137-99EF-80FA5C83896B} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{9DA9AED3-9572-4378-A2A6-4D792D67ADDC} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{FD4969DB-3CCE-4CCF-BAE4-1BE8A3F40812} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
		{0947E6C0-6371-4483-B0ED-191FB5073151} = {E65490B8-D2E2-4693-B39C-15703B1EBFBB}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
EndGlobal
 | 
			
		||||
							
								
								
									
										25
									
								
								framework/ThingsGateway.sln.licenseheader
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,25 @@
 | 
			
		||||
extensions: .cs 
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
extensions: .razor 
 | 
			
		||||
@*
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
*@
 | 
			
		||||
							
								
								
									
										19
									
								
								framework/admin/Directory.Build.props
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,19 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<ImplicitUsings>enable</ImplicitUsings>
 | 
			
		||||
		<TargetFrameworks>net6.0;net8.0;</TargetFrameworks>
 | 
			
		||||
		<Version>4.0.0.4</Version>
 | 
			
		||||
		<LangVersion>latest</LangVersion>
 | 
			
		||||
		<ImplicitUsings>enable</ImplicitUsings>
 | 
			
		||||
		<Authors>Diego</Authors>
 | 
			
		||||
		<Product>ThingsGateway</Product>
 | 
			
		||||
		<Copyright>© 2023-present Diego</Copyright>
 | 
			
		||||
		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
 | 
			
		||||
		<SignAssembly>True</SignAssembly>
 | 
			
		||||
		<DelaySign>False</DelaySign>
 | 
			
		||||
		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.ApiController;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 后台登录控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
 | 
			
		||||
[Route("auth/b")]
 | 
			
		||||
[LoggingMonitor]
 | 
			
		||||
public class AuthController : IDynamicApiController
 | 
			
		||||
{
 | 
			
		||||
    private readonly IAuthService _authService;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="AuthController"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="authService"></param>
 | 
			
		||||
    public AuthController(IAuthService authService)
 | 
			
		||||
    {
 | 
			
		||||
        _authService = authService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 后台登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [AllowAnonymous]
 | 
			
		||||
    [HttpPost("login")]
 | 
			
		||||
    [Description(EventSubscriberConst.Login)]
 | 
			
		||||
    public async Task<LoginOutput> LoginAsync(LoginInput input)
 | 
			
		||||
    {
 | 
			
		||||
        return await _authService.LoginAsync(input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 后台登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpPost("logout")]
 | 
			
		||||
    [Description(EventSubscriberConst.Logout)]
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    public async Task LogoutAsync()
 | 
			
		||||
    {
 | 
			
		||||
        await _authService.LogoutAsync();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,67 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.ApiController;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 文件下载
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
 | 
			
		||||
[Route("file")]
 | 
			
		||||
[LoggingMonitor]
 | 
			
		||||
public class FileController : IDynamicApiController
 | 
			
		||||
{
 | 
			
		||||
    private readonly IOperateLogService _operateLogService;
 | 
			
		||||
    private readonly IVisitLogService _visitLogService;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="FileController"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public FileController(
 | 
			
		||||
        IOperateLogService operateLogService,
 | 
			
		||||
        IVisitLogService visitLogService
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _operateLogService = operateLogService;
 | 
			
		||||
        _visitLogService = visitLogService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载操作日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpGet("operateLog")]
 | 
			
		||||
    public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var memoryStream = await _operateLogService.ExportFileAsync(input);
 | 
			
		||||
        var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
 | 
			
		||||
        {
 | 
			
		||||
            FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
 | 
			
		||||
        };
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载访问日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpGet("visitLog")]
 | 
			
		||||
    public async Task<IActionResult> DownloadVisitLogAsync([FromQuery] VisitLogInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var memoryStream = await _visitLogService.ExportFileAsync(input);
 | 
			
		||||
        var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
 | 
			
		||||
        {
 | 
			
		||||
            FileDownloadName = $"operateLog{DateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
 | 
			
		||||
        };
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,63 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.ApiController;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// OpenApi登录控制器
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayOpenApi, Order = 200)]
 | 
			
		||||
[Route("auth/openapi")]
 | 
			
		||||
[LoggingMonitor]
 | 
			
		||||
[Description("OpenApi登录")]
 | 
			
		||||
[Authorize(AuthenticationSchemes = "Bearer")]
 | 
			
		||||
public class OpenApiAuthController : IDynamicApiController
 | 
			
		||||
{
 | 
			
		||||
    private readonly IOpenApiAuthService _authService;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="OpenApiAuthController"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="authService"></param>
 | 
			
		||||
    public OpenApiAuthController(IOpenApiAuthService authService)
 | 
			
		||||
    {
 | 
			
		||||
        _authService = authService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpenApi登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [AllowAnonymous]
 | 
			
		||||
    [HttpPost("login")]
 | 
			
		||||
    [Description(EventSubscriberConst.LoginOpenApi)]
 | 
			
		||||
    public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
 | 
			
		||||
    {
 | 
			
		||||
        return await _authService.LoginOpenApiAsync(input);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpPost("logout")]
 | 
			
		||||
    [Description(EventSubscriberConst.LogoutOpenApi)]
 | 
			
		||||
    public async Task LogoutAsync()
 | 
			
		||||
    {
 | 
			
		||||
        await _authService.LogoutAsync();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,69 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.SpecificationDocument;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.ApiController;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Swagger登录授权服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
 | 
			
		||||
[Route("Swagger")]
 | 
			
		||||
public class SwaggerController : IDynamicApiController, IScoped
 | 
			
		||||
{
 | 
			
		||||
    private readonly ConfigService _configService;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="SwaggerController"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysConfigService"></param>
 | 
			
		||||
    public SwaggerController(ConfigService sysConfigService)
 | 
			
		||||
    {
 | 
			
		||||
        _configService = sysConfigService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Swagger登录检查
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpPost("CheckUrl")]
 | 
			
		||||
    [AllowAnonymous, NonUnify]
 | 
			
		||||
    public async Task<int> SwaggerCheckUrlAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var enable = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGERLOGIN_OPEN)).ConfigValue.ToBool(false);
 | 
			
		||||
        return enable ? 401 : 200;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Swagger登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="auth"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpPost("SubmitUrl")]
 | 
			
		||||
    [AllowAnonymous, NonUnify]
 | 
			
		||||
    public async Task<int> SwaggerSubmitUrlAsync([FromForm] SpecificationAuth auth)
 | 
			
		||||
    {
 | 
			
		||||
        var userName = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_NAME)).ConfigValue;
 | 
			
		||||
        var password = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SWAGGER_PASSWORD)).ConfigValue;
 | 
			
		||||
        if (auth.UserName == userName && auth.Password == password)
 | 
			
		||||
        {
 | 
			
		||||
            return 200;
 | 
			
		||||
        }
 | 
			
		||||
        return 401;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,19 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using Furion.DynamicApiController;
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Admin.Application;
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
 | 
			
		||||
		<GenerateDocumentationFile>True</GenerateDocumentationFile>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
	
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -0,0 +1,102 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Admin.ApiController</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.ApiController.AuthController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登录控制器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.AuthController.#ctor(ThingsGateway.Admin.Application.IAuthService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.ApiController.AuthController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="authService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.AuthController.LoginAsync(ThingsGateway.Admin.Application.LoginInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="input"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.AuthController.LogoutAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登出
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.ApiController.FileController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            文件下载
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.FileController.#ctor(ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.ApiController.FileController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.FileController.DownloadOperateLogAsync(ThingsGateway.Admin.Application.OperateLogInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            下载操作日志
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.FileController.DownloadVisitLogAsync(ThingsGateway.Admin.Application.VisitLogInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            下载访问日志
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.ApiController.OpenApiAuthController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpenApi登录控制器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.#ctor(ThingsGateway.Admin.Application.IOpenApiAuthService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.ApiController.OpenApiAuthController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="authService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.LoginOpenApiAsync(ThingsGateway.Admin.Application.LoginOpenApiInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpenApi登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="input"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.OpenApiAuthController.LogoutAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            登出
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.ApiController.SwaggerController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录授权服务
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.SwaggerController.#ctor(ThingsGateway.Admin.Application.ConfigService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.ApiController.SwaggerController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="sysConfigService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.SwaggerController.SwaggerCheckUrlAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录检查
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.ApiController.SwaggerController.SwaggerSubmitUrlAsync(Furion.SpecificationDocument.SpecificationAuth)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="auth"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 操作事件说明特性
 | 
			
		||||
/// </summary>
 | 
			
		||||
[AttributeUsage(AttributeTargets.Method)]
 | 
			
		||||
public class OperDescAttribute : Attribute
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作记录标识
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="description"></param>
 | 
			
		||||
    /// <param name="catcategory"></param>
 | 
			
		||||
    public OperDescAttribute(string description, string catcategory = LogConst.LOG_OPERATE)
 | 
			
		||||
    {
 | 
			
		||||
        Description = description;
 | 
			
		||||
        Catcategory = catcategory;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分类
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Catcategory { get; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 说明
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Description { get; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 记录参数,默认true
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool IsRecordPar { get; set; } = true;
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,224 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.Reflection;
 | 
			
		||||
using Furion.Reflection.Extensions;
 | 
			
		||||
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
using UAParser;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// AOP处理操作日志
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OperDispatchProxy : AspectDispatchProxy, IDispatchProxy
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 服务提供器,可以用来解析服务,如:Services.GetService()
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public IServiceProvider Services { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 当前服务实例
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public object Target { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 方法
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="method"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override object Invoke(MethodInfo method, object[] args)
 | 
			
		||||
    {
 | 
			
		||||
        var desc = Target.GetCustomAttribute<OperDescAttribute>(method.ToString(), true);
 | 
			
		||||
        if (desc == null)
 | 
			
		||||
        {
 | 
			
		||||
            return Invoke(method, args);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            Exception exception = default;
 | 
			
		||||
            object result = default;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                result = Invoke(method, args);
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            WriteOperLog(method, args, desc, result, exception);
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
            {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
            return result;//返回结果
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        object Invoke(MethodInfo method, object[] args)
 | 
			
		||||
        {
 | 
			
		||||
            //如果不带返回值
 | 
			
		||||
            if (method.ReturnType == typeof(void))
 | 
			
		||||
            {
 | 
			
		||||
                return method.Invoke(Target, args);//直接返回
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                var result = method.Invoke(Target, args);
 | 
			
		||||
                return result;//返回结果
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 异步无返回值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="method"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    /// <exception cref="NotImplementedException"></exception>
 | 
			
		||||
    public override async Task InvokeAsync(MethodInfo method, object[] args)
 | 
			
		||||
    {
 | 
			
		||||
        var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target);
 | 
			
		||||
        if (desc == null)
 | 
			
		||||
        {
 | 
			
		||||
            var task = method.Invoke(Target, args) as Task;
 | 
			
		||||
            await task;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            Exception exception = default;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var task = method.Invoke(Target, args) as Task;
 | 
			
		||||
                await task;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            WriteOperLog(method, args, desc, null, exception);
 | 
			
		||||
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
            {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 异步带返回值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="method"></param>
 | 
			
		||||
    /// <param name="args"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    /// <exception cref="NotImplementedException"></exception>
 | 
			
		||||
    public override async Task<T> InvokeAsyncT<T>(MethodInfo method, object[] args)
 | 
			
		||||
    {
 | 
			
		||||
        var desc = method.GetActualCustomAttribute<OperDescAttribute>(Target);
 | 
			
		||||
        if (desc == null)
 | 
			
		||||
        {
 | 
			
		||||
            var taskT = method.Invoke(Target, args) as Task<T>;
 | 
			
		||||
            var result = await taskT;
 | 
			
		||||
            return result;//返回结果
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            T result = default;
 | 
			
		||||
            //写入操作日志
 | 
			
		||||
            Exception exception = null;
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var taskT = method.Invoke(Target, args) as Task<T>;
 | 
			
		||||
                result = await taskT;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                exception = ex;
 | 
			
		||||
            }
 | 
			
		||||
            WriteOperLog(method, args, desc, result, exception);
 | 
			
		||||
            if (exception != null)
 | 
			
		||||
            {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return result;//返回结果
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private void WriteOperLog(MethodInfo method, object[] args, OperDescAttribute desc, object result, Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        //写入操作日志
 | 
			
		||||
        var str = App.HttpContext?.Request?.Headers?.UserAgent;
 | 
			
		||||
        ClientInfo clientInfo = null;
 | 
			
		||||
        if (str.HasValue)
 | 
			
		||||
        {
 | 
			
		||||
            var uaParser = Parser.GetDefault();
 | 
			
		||||
            clientInfo = uaParser.Parse(str);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        StringBuilder stringBuilder = new();
 | 
			
		||||
        if (desc.IsRecordPar)
 | 
			
		||||
        {
 | 
			
		||||
            var parameters = method.GetParameters();
 | 
			
		||||
            var jsonParameters = parameters.Select((p, i) => $"\"{p.Name}\": {args[i].ToJsonString()}");
 | 
			
		||||
            stringBuilder.Append('{');
 | 
			
		||||
            stringBuilder.Append(string.Join(", ", jsonParameters));
 | 
			
		||||
            stringBuilder.Append('}');
 | 
			
		||||
        }
 | 
			
		||||
        var paramJson = stringBuilder.ToString();
 | 
			
		||||
        var resultJson = desc.IsRecordPar ? result?.ToJsonString() : null;
 | 
			
		||||
        //操作日志表实体
 | 
			
		||||
        var log = new SysOperateLog
 | 
			
		||||
        {
 | 
			
		||||
            Name = desc.Description,
 | 
			
		||||
            Category = desc.Catcategory,
 | 
			
		||||
            ExeStatus = LogConst.LOG_SUCCESS,
 | 
			
		||||
            OpIp = App.HttpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString(),
 | 
			
		||||
            OpBrowser = clientInfo?.UA?.Family + clientInfo?.UA?.Major,
 | 
			
		||||
            OpOs = clientInfo?.OS?.Family + clientInfo?.OS?.Major,
 | 
			
		||||
            OpTime = DateTimeExtensions.CurrentDateTime,
 | 
			
		||||
            OpAccount = UserManager.UserAccount,
 | 
			
		||||
            ReqUrl = "",
 | 
			
		||||
            ReqMethod = LogConst.LOG_REQMETHOD,
 | 
			
		||||
            ResultJson = resultJson,
 | 
			
		||||
            ClassName = method.ReflectedType.Name,
 | 
			
		||||
            MethodName = method.Name,
 | 
			
		||||
            ParamJson = paramJson,
 | 
			
		||||
            VerificatId = UserManager.VerificatId.ToLong(),
 | 
			
		||||
        };
 | 
			
		||||
        //如果异常不为空
 | 
			
		||||
        if (exception != null)
 | 
			
		||||
        {
 | 
			
		||||
            log.ExeStatus = LogConst.LOG_FAIL;//操作状态为失败
 | 
			
		||||
            log.ExeMessage = exception.Source + ":" + exception.Message + Environment.NewLine + exception.StackTrace;
 | 
			
		||||
        }
 | 
			
		||||
        DbContext.Db.CopyNew().InsertableWithAttr(log).ExecuteCommand();//入库
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 权限操作常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class AdminConst
 | 
			
		||||
{
 | 
			
		||||
    #region 操作
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 禁用操作
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Disable = "禁用";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启用操作
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Enable = "启用";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户授权操作
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string GrantRole = "授权";
 | 
			
		||||
 | 
			
		||||
    #endregion 操作
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,83 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Cache常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class CacheConst
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录验证码缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOGIN_CAPTCHA = "LOGIN_CAPTCHA";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SYS_CONFIGCATEGORY = "SYS_CONFIGCATEGORY";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region OpenApi
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpenApi用户表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_OPENAPIUSER = "CACHE_OPENAPIUSER";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpenApi关系缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_OPENAPIUSERACCOUNT = "CACHE_OPENAPIUSERACCOUNT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// UserVerificat缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_OPENAPIUSERVERIFICAT = "CACHE_OPENAPIUSERVERIFICAT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// UserVerificat缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_USERVERIFICAT = "CACHE_USERVERIFICAT";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #endregion OpenApi
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_SYSUSER = "CACHE_SYSUSER";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CAHCE_SYSUSERACCOUNT = "CAHCE_SYSUSERACCOUNT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 关系表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_SYSRELATION = "CACHE_SYSRELATION";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 资源表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_SYSRESOURCE = "CACHE_SYSRESOURCE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色表缓存Key
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CACHE_SYSROLE = "CACHE_SYSROLE";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 其他分类常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class CateGoryConst
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ThingsGateway.Admin
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string ThingsGatewayAdmin = "ThingsGateway.Admin";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ThingsGateway.OpenApi
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string ThingsGatewayOpenApi = "ThingsGateway.OpenApi";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region 关系表
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户主页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_USER_DEFAULTRAZOR = "Relation_SYS_USER_DEFAULTRAZOR";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户工作台数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_USER_WORKBENCH_DATA = "SYS_USER_WORKBENCH_DATA";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色有哪些权限
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色有哪些资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户有哪些角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
 | 
			
		||||
    #endregion 关系表
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,96 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 配置常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class ConfigConst
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 系统固定配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SYS_CONFIGBASEDEFAULT = "SYS_CONFIGBASEDEFAULT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 其他自定义配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SYS_CONFIGOTHER = "SYS_CONFIGOTHER";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #region config
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 版权标识
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_COPYRIGHT = "CONFIG_COPYRIGHT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 版权跳转url
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_COPYRIGHT_URL = "CONFIG_COPYRIGHT_URL";
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 是否启用PageTab
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_PAGETAB = "CONFIG_PAGETAB";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录验证码开关
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_CAPTCHA_OPEN = "CONFIG_CAPTCHA_OPEN";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 默认用户密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_PASSWORD = "CONFIG_PASSWORD";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录界面的介绍文本
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_REMARK = "CONFIG_REMARK";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 单用户登录开关
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_SINGLE_OPEN = "CONFIG_SINGLE_OPEN";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// swagger用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_SWAGGER_NAME = "CONFIG_SWAGGER_NAME";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// swagger密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_SWAGGER_PASSWORD = "CONFIG_SWAGGER_PASSWORD";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 系统标题
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_TITLE = "CONFIG_TITLE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 系统登录过期时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_VERIFICAT_EXPIRES = "CONFIG_VERIFICAT_EXPIRES";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Swagger是否需要登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string CONFIG_SWAGGERLOGIN_OPEN = "CONFIG_SWAGGERLOGIN_OPEN";
 | 
			
		||||
 | 
			
		||||
    #endregion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 事件总线常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class EventSubscriberConst
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 清除用户缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string ClearUserCache = "清除用户缓存";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 页面登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Login = "登录";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpenApi登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LoginOpenApi = "OpenApi登录";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 后台登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Logout = "退出";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// OpenApi登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LogoutOpenApi = "OpenApi退出";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 通讯器常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class HubConst
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 系统HubUrl
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SysHubUrl = "/hubs/thingsgateway";
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,69 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 日志常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LogConst
 | 
			
		||||
{
 | 
			
		||||
    #region 日志表
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_LOGIN = "LOGIN";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_LOGOUT = "LOGOUT";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 第三方登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_OPENAPILOGIN = "OPENAPILOGIN";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 第三方登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_OPENAPILOGOUT = "OPENAPILOGOUT";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 第三方操作来源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_OPENAPIOPERATE = "OPENAPIOPERATE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作分类
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_OPERATE = "OPERATE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 内部操作来源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_REQMETHOD = "BLAZORSERVER";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作成功
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_SUCCESS = "SUCCESS";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作失败
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string LOG_FAIL = "FAIL";
 | 
			
		||||
    #endregion 日志表
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 资源表常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ResourceConst
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 系统内置编码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string System = "system";
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色常量
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RoleConst
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 超级管理员
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SuperAdmin = "superAdmin";
 | 
			
		||||
 | 
			
		||||
    #region 关系表
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色有哪些权限
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_ROLE_HAS_PERMISSION = "SYS_ROLE_HAS_PERMISSION";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色有哪些资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_ROLE_HAS_RESOURCE = "SYS_ROLE_HAS_RESOURCE";
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户有哪些角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Relation_SYS_USER_HAS_ROLE = "SYS_USER_HAS_ROLE";
 | 
			
		||||
 | 
			
		||||
    #endregion 关系表
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
global using Furion;
 | 
			
		||||
 | 
			
		||||
global using System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.IO;
 | 
			
		||||
global using System.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
global using ThingsGateway.Admin.Core;
 | 
			
		||||
global using ThingsGateway.Core;
 | 
			
		||||
global using ThingsGateway.Foundation.Core;
 | 
			
		||||
@@ -0,0 +1,68 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.Schedule;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IJobPersistence"/>
 | 
			
		||||
public class JobPersistence : IJobPersistence
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public JobPersistence(IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope?.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void OnChanged(PersistenceContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业计划初始化通知
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="builder"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public SchedulerBuilder OnLoading(SchedulerBuilder builder)
 | 
			
		||||
    {
 | 
			
		||||
        return builder;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void OnTriggerChanged(PersistenceTriggerContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业调度服务启动时
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public IEnumerable<SchedulerBuilder> Preload()
 | 
			
		||||
    {
 | 
			
		||||
        // 获取所有定义的作业
 | 
			
		||||
        var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
 | 
			
		||||
        return allJobs;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,32 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.Schedule;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 清理日志作业任务
 | 
			
		||||
/// </summary>
 | 
			
		||||
[JobDetail("job_log", Description = "清理访问/操作日志", GroupName = "default", Concurrent = false)]
 | 
			
		||||
[Daily(TriggerId = "trigger_log", Description = "清理访问/操作日志", RunOnStart = true)]
 | 
			
		||||
public class LogJob : IJob
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
 | 
			
		||||
    {
 | 
			
		||||
        var db = DbContext.Db.CopyNew();
 | 
			
		||||
        var daysAgo = App.GetConfig<int?>("Logging:LogJob:DaysAgo") ?? 30;
 | 
			
		||||
        await db.DeleteableWithAttr<SysVisitLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除访问日志
 | 
			
		||||
        await db.DeleteableWithAttr<SysOperateLog>().Where(u => u.CreateTime < DateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(stoppingToken); // 删除操作日志
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录事件参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginOpenApiEvent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime DateTime = DateTimeExtensions.CurrentDateTime;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录设备
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public AuthDeviceTypeEnum Device { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 过期时间(分)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Expire { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Ip地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Ip { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public OpenApiUser OpenApiUser { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long VerificatId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,52 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 认证模块事件总线
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiAuthEventSubscriber : IEventSubscriber, ISingleton
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录事件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [EventSubscribe(EventSubscriberConst.LoginOpenApi)]
 | 
			
		||||
    public async Task LoginOpenApi(EventHandlerExecutingContext context)
 | 
			
		||||
    {
 | 
			
		||||
        LoginOpenApiEvent loginEvent = (LoginOpenApiEvent)context.Source.Payload;//获取参数
 | 
			
		||||
        OpenApiUser openApiUser = loginEvent.OpenApiUser;
 | 
			
		||||
        var db = DbContext.Db.CopyNew();
 | 
			
		||||
        #region 重新赋值属性,设置本次登录信息为最新的信息
 | 
			
		||||
 | 
			
		||||
        db.Tracking(openApiUser);//创建跟踪,只更新修改字段
 | 
			
		||||
        openApiUser.LastLoginDevice = openApiUser.LatestLoginDevice;
 | 
			
		||||
        openApiUser.LastLoginIp = openApiUser.LatestLoginIp;
 | 
			
		||||
        openApiUser.LastLoginTime = openApiUser.LatestLoginTime;
 | 
			
		||||
        openApiUser.LatestLoginDevice = loginEvent.Device.ToString();
 | 
			
		||||
        openApiUser.LatestLoginIp = loginEvent.Ip;
 | 
			
		||||
        openApiUser.LatestLoginTime = loginEvent.DateTime;
 | 
			
		||||
 | 
			
		||||
        #endregion 重新赋值属性,设置本次登录信息为最新的信息
 | 
			
		||||
 | 
			
		||||
        //更新用户信息
 | 
			
		||||
        if (await db.UpdateableWithAttr(openApiUser).ExecuteCommandAsync() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            App.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSER + openApiUser.Id, openApiUser, false); //更新Cache信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,33 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录输入参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginOpenApiInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 密码
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,24 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录返回参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginOpenApiOutput : BaseLoginOutput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// TOKEN
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Token { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,33 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IOpenApiAuthService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">登录参数</param>
 | 
			
		||||
    /// <returns>Token信息</returns>
 | 
			
		||||
    Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登出
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task LogoutAsync();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,172 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DataEncryption;
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IOpenApiAuthService"/>
 | 
			
		||||
public class OpenApiAuthService : IOpenApiAuthService, ITransient
 | 
			
		||||
{
 | 
			
		||||
    private readonly IConfigService _configService;
 | 
			
		||||
    private readonly IEventPublisher _eventPublisher;
 | 
			
		||||
    private readonly IOpenApiUserService _openApiUserService;
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IOpenApiAuthService"/>
 | 
			
		||||
    public OpenApiAuthService(
 | 
			
		||||
                       IEventPublisher eventPublisher,
 | 
			
		||||
                       IOpenApiUserService openApiUserService,
 | 
			
		||||
                       IVerificatService verificatService,
 | 
			
		||||
                       IConfigService configService)
 | 
			
		||||
    {
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
        _eventPublisher = eventPublisher;
 | 
			
		||||
        _openApiUserService = openApiUserService;
 | 
			
		||||
        _configService = configService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<LoginOpenApiOutput> LoginOpenApiAsync(LoginOpenApiInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var password = input.Password;
 | 
			
		||||
        var userInfo = await _openApiUserService.GetUserByAccountAsync(input.Account);//获取用户信息
 | 
			
		||||
        if (userInfo == null) throw Oops.Bah("用户不存在");//用户不存在
 | 
			
		||||
        if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误
 | 
			
		||||
        return await PrivateLoginOpenApiAsync(userInfo);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task LogoutAsync()
 | 
			
		||||
    {
 | 
			
		||||
        //获取用户信息
 | 
			
		||||
        var userinfo = await _openApiUserService.GetUserByAccountAsync(UserManager.UserAccount);
 | 
			
		||||
        if (userinfo != null)
 | 
			
		||||
        {
 | 
			
		||||
            LoginOpenApiEvent loginEvent = new()
 | 
			
		||||
            {
 | 
			
		||||
                Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
 | 
			
		||||
                OpenApiUser = userinfo,
 | 
			
		||||
                VerificatId = UserManager.VerificatId.ToLong(),
 | 
			
		||||
            };
 | 
			
		||||
            await RemoveVerificatFromCacheAsync(loginEvent);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<List<VerificatInfo>> GetVerificatInfos(long userId)
 | 
			
		||||
    {
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(userId);
 | 
			
		||||
        return verificatInfos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task<LoginOpenApiOutput> PrivateLoginOpenApiAsync(OpenApiUser openApiUser)
 | 
			
		||||
    {
 | 
			
		||||
        if (openApiUser.UserEnable == false) throw Oops.Bah("账号已停用");//账号冻结
 | 
			
		||||
        var sessionid = YitIdHelper.NextId();
 | 
			
		||||
        var expire = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_VERIFICAT_EXPIRES)).ConfigValue.ToInt();
 | 
			
		||||
        //生成Token
 | 
			
		||||
        var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
 | 
			
		||||
    {
 | 
			
		||||
        {ClaimConst.UserId, openApiUser.Id},
 | 
			
		||||
        {ClaimConst.Account, openApiUser.Account},
 | 
			
		||||
        { ClaimConst.VerificatId, sessionid.ToString()},
 | 
			
		||||
        { ClaimConst.IsOpenApi, true},
 | 
			
		||||
      }, expire);
 | 
			
		||||
        // 生成刷新Token令牌
 | 
			
		||||
        var refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, expire * 2);
 | 
			
		||||
        // 设置Swagger自动登录
 | 
			
		||||
        App.HttpContext.SigninToSwagger(accessToken);
 | 
			
		||||
        // 设置响应报文头
 | 
			
		||||
        App.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
 | 
			
		||||
        //登录事件参数
 | 
			
		||||
        var logingEvent = new LoginOpenApiEvent
 | 
			
		||||
        {
 | 
			
		||||
            Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
 | 
			
		||||
            Device = AuthDeviceTypeEnum.Api,
 | 
			
		||||
            Expire = expire,
 | 
			
		||||
            OpenApiUser = openApiUser,
 | 
			
		||||
            VerificatId = sessionid
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await WriteVerificatToCacheAsync(logingEvent);//写入verificat到cache
 | 
			
		||||
        await _eventPublisher.PublishAsync(EventSubscriberConst.LoginOpenApi, logingEvent); //发布登录事件总线
 | 
			
		||||
                                                                                            //返回结果
 | 
			
		||||
        return new LoginOpenApiOutput { VerificatId = sessionid, Token = accessToken, Account = openApiUser.Account };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task RemoveVerificatFromCacheAsync(LoginOpenApiEvent loginEvent)
 | 
			
		||||
    {
 | 
			
		||||
        //获取verificat列表
 | 
			
		||||
        var verificatInfos = await GetVerificatInfos(loginEvent.OpenApiUser.Id);
 | 
			
		||||
        if (verificatInfos != null)
 | 
			
		||||
        {
 | 
			
		||||
            //获取当前用户的verificat
 | 
			
		||||
            var verificat = verificatInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault();
 | 
			
		||||
            if (verificat != null)
 | 
			
		||||
                verificatInfos.Remove(verificat);
 | 
			
		||||
            //更新verificat列表
 | 
			
		||||
            await _verificatService.SetOpenApiVerificatIdAsync(loginEvent.OpenApiUser.Id, verificatInfos);
 | 
			
		||||
        }
 | 
			
		||||
        await App.HttpContext?.SignOutAsync();
 | 
			
		||||
        App.HttpContext?.SignoutToSwagger();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private async Task WriteVerificatToCacheAsync(LoginOpenApiEvent loginEvent)
 | 
			
		||||
    {
 | 
			
		||||
        //获取verificat列表
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await GetVerificatInfos(loginEvent.OpenApiUser.Id);
 | 
			
		||||
        var verificatTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
 | 
			
		||||
        //生成verificat信息
 | 
			
		||||
        var verificatInfo = new VerificatInfo
 | 
			
		||||
        {
 | 
			
		||||
            Device = loginEvent.Device.ToString(),
 | 
			
		||||
            Expire = loginEvent.Expire,
 | 
			
		||||
            VerificatTimeout = verificatTimeout,
 | 
			
		||||
            Id = loginEvent.VerificatId,
 | 
			
		||||
            UserId = loginEvent.OpenApiUser.Id,
 | 
			
		||||
        };
 | 
			
		||||
        if (verificatInfos != null)
 | 
			
		||||
        {
 | 
			
		||||
            bool isSingle = false;//默认不开启单用户登录
 | 
			
		||||
            var singleConfig = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SINGLE_OPEN);//获取系统单用户登录选项
 | 
			
		||||
            if (singleConfig != null) isSingle = singleConfig.ConfigValue.ToBool(false);//如果配置不为空则设置单用户登录选项为系统配置的值
 | 
			
		||||
 | 
			
		||||
            //判断是否单用户登录
 | 
			
		||||
            if (isSingle)
 | 
			
		||||
            {
 | 
			
		||||
                verificatInfos = verificatInfos.ToList();//去掉当前登录类型的verificat
 | 
			
		||||
                verificatInfos.Add(verificatInfo);//添加到列表
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                verificatInfos.Add(verificatInfo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            verificatInfos = new List<VerificatInfo> { verificatInfo };//直接就一个
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //添加到verificat列表
 | 
			
		||||
        await _verificatService.SetOpenApiVerificatIdAsync(loginEvent.OpenApiUser.Id, verificatInfos);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话分页查询
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiSessionPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("账号")]
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录IP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("最新登录IP")]
 | 
			
		||||
    public string LatestLoginIp { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 退出参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiExitVerificatInput : BaseIdInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证ID列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "VerificatIds不能为空")]
 | 
			
		||||
    public List<long> VerificatIds { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,55 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话输出
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiSessionOutput : PrimaryKeyEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("账号")]
 | 
			
		||||
    [DataTable(Order = 1, IsShow = true, Sortable = true)]
 | 
			
		||||
    public virtual string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录ip
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("最新登录ip")]
 | 
			
		||||
    [DataTable(Order = 2, IsShow = true, Sortable = true)]
 | 
			
		||||
    public string LatestLoginIp { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录时间
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("最新登录时间")]
 | 
			
		||||
    [DataTable(Order = 3, IsShow = true, Sortable = true)]
 | 
			
		||||
    public DateTime? LatestLoginTime { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌数量
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌数量")]
 | 
			
		||||
    [DataTable(Order = 4, IsShow = true, Sortable = false)]
 | 
			
		||||
    public int VerificatCount { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌信息集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌列表")]
 | 
			
		||||
    public List<VerificatInfo> VerificatSignList { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话管理服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IOpenApiSessionService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 强退会话
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户ID</param>
 | 
			
		||||
    Task ExitSessionAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 强退cookie
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">cookie列表</param>
 | 
			
		||||
    Task ExitVerificatAsync(OpenApiExitVerificatInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// B端会话分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>B端会话列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<OpenApiSessionOutput>> PageAsync(OpenApiSessionPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,106 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IOpenApiSessionService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class OpenApiSessionService : DbRepository<OpenApiUser>, IOpenApiSessionService
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IOpenApiSessionService"/>
 | 
			
		||||
    public OpenApiSessionService(IVerificatService verificatService)
 | 
			
		||||
    {
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("强退OPENAPI会话")]
 | 
			
		||||
    public async Task ExitSessionAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        //从列表中删除
 | 
			
		||||
        await _verificatService.SetOpenApiVerificatIdAsync(input, new());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("强退OPENAPI令牌")]
 | 
			
		||||
    public async Task ExitVerificatAsync(OpenApiExitVerificatInput input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取该用户的verificat信息
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetOpenApiVerificatIdAsync(input.Id);
 | 
			
		||||
        //当前需要踢掉用户的verificat
 | 
			
		||||
        var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
 | 
			
		||||
        await _verificatService.SetOpenApiVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取verificat剩余时间信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="verificatInfos">verificat列表</param>
 | 
			
		||||
    private static void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
 | 
			
		||||
    {
 | 
			
		||||
        verificatInfos = verificatInfos.ToList();
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            var now = DateTimeExtensions.CurrentDateTime;
 | 
			
		||||
            it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
 | 
			
		||||
            var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
 | 
			
		||||
            var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<OpenApiSessionOutput>> PageAsync(OpenApiSessionPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<OpenApiUser>()
 | 
			
		||||
            .WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
 | 
			
		||||
            .WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
 | 
			
		||||
            .OrderBy(it => it.LatestLoginTime, OrderByType.Desc)
 | 
			
		||||
            .Select<OpenApiSessionOutput>()
 | 
			
		||||
            .Mapper(async it =>
 | 
			
		||||
            {
 | 
			
		||||
                var verificatInfos = await _verificatService.GetVerificatIdAsync(it.Id);
 | 
			
		||||
                if (verificatInfos != null)
 | 
			
		||||
                {
 | 
			
		||||
                    OpenApiSessionService.GetVerificatInfos(ref verificatInfos);//获取剩余时间
 | 
			
		||||
                    it.VerificatCount = verificatInfos.Count;//令牌数量
 | 
			
		||||
                    it.VerificatSignList = verificatInfos;//令牌列表
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    it.VerificatSignList = new();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        pageInfo.Records = pageInfo.Records.OrderByDescending(it => it.VerificatCount);
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,148 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Api授权资源树
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiPermissionTreeSelector
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 接口描述
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Api说明")]
 | 
			
		||||
    public string ApiName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 路由名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Api路径")]
 | 
			
		||||
    public string ApiRoute { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 子节点
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public List<OpenApiPermissionTreeSelector> Children { get; set; } = new();
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long Id { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long ParentId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 权限名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("权限名称")]
 | 
			
		||||
    public string PermissionName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多个树转列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static List<OpenApiPermissionTreeSelector> TreeToList(IList<OpenApiPermissionTreeSelector> data)
 | 
			
		||||
    {
 | 
			
		||||
        List<OpenApiPermissionTreeSelector> list = new();
 | 
			
		||||
        foreach (var item in data)
 | 
			
		||||
        {
 | 
			
		||||
            list.Add(item);
 | 
			
		||||
            if (item.Children != null && item.Children.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                list.AddRange(TreeToList(item.Children));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 添加用户参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiUserAddInput : OpenApiUser
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public override string Account { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
 | 
			
		||||
    public override string Password { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override bool UserEnable { get; set; } = true;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 编辑用户参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiUserEditInput : OpenApiUser
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public override string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "密码不能为空"), MinLength(2, ErrorMessage = "密码不能少于3个字符")]
 | 
			
		||||
    public override string Password { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户分页查询参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiUserPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 动态查询条件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Expressionable<SysUser> Expression { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户授权参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OpenApiUserGrantPermissionInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public long? Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 授权权限信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "PermissionList不能为空")]
 | 
			
		||||
    public List<string> PermissionList { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,104 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IOpenApiUserService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(OpenApiUserAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">Id列表</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 从cache中删除用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ids">用户ID列表</param>
 | 
			
		||||
    void DeleteUserFromCache(params long[] ids);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 禁用用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户Id</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DisableUserAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">编辑参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(OpenApiUserEditInput input);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启用用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户Id</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EnableUserAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///根据用户账号获取用户ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="account">用户账号</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<long> GetIdByAccountAsync(string account);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据账号获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="account">用户名</param>
 | 
			
		||||
    /// <returns>用户信息</returns>
 | 
			
		||||
    Task<OpenApiUser> GetUserByAccountAsync(string account);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据ID获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="Id"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<OpenApiUser> GetUsertByIdAsync(long Id);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 给用户授权
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">授权参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task GrantRoleAsync(OpenApiUserGrantPermissionInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取用户拥有权限,返回的是服务方法名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<string>> OwnPermissionsAsync(BaseIdInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>用户分页列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,284 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DataEncryption;
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IOpenApiUserService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class OpenApiUserService : DbRepository<OpenApiUser>, IOpenApiUserService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IOpenApiUserService"/>
 | 
			
		||||
    public OpenApiUserService(
 | 
			
		||||
        IVerificatService verificatService, IServiceScopeFactory serviceScopeFactory
 | 
			
		||||
                      )
 | 
			
		||||
    {
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("添加用户")]
 | 
			
		||||
    public async Task AddAsync(OpenApiUserAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var account_Id = await GetIdByAccountAsync(input.Account);
 | 
			
		||||
        if (account_Id > 0)
 | 
			
		||||
            throw Oops.Bah($"存在重复的账号:{input.Account}");
 | 
			
		||||
 | 
			
		||||
        var openApiUser = input.Adapt<OpenApiUser>();//实体转换
 | 
			
		||||
        await InsertAsync(openApiUser);//添加数据
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("删除用户")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] ids)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        //获取所有ID
 | 
			
		||||
        if (ids.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var result = await DeleteByIdsAsync(ids.Cast<object>().ToArray());
 | 
			
		||||
            if (result)
 | 
			
		||||
            {
 | 
			
		||||
                //从列表中删除
 | 
			
		||||
                foreach (var id in ids)
 | 
			
		||||
                {
 | 
			
		||||
                    await _verificatService.SetOpenApiVerificatIdAsync(id, new());
 | 
			
		||||
                }
 | 
			
		||||
                DeleteUserFromCache(ids);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public void DeleteUserFromCache(params long[] ids)
 | 
			
		||||
    {
 | 
			
		||||
        var userIds = ids.Select(it => it.ToString()).ToArray();//id转string列表
 | 
			
		||||
        List<OpenApiUser> openApiUsers = new();
 | 
			
		||||
        foreach (var item in userIds)
 | 
			
		||||
        {
 | 
			
		||||
            var user = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<OpenApiUser>(CacheConst.CACHE_OPENAPIUSER + item, false);//获取用户列表
 | 
			
		||||
            openApiUsers.Add(user);
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_OPENAPIUSER + item);
 | 
			
		||||
        }
 | 
			
		||||
        openApiUsers = openApiUsers.Where(it => it != null).ToList();//过滤掉不存在的
 | 
			
		||||
        if (openApiUsers.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var accounts = openApiUsers.Select(it => it.Account).ToArray();//账号集合
 | 
			
		||||
            foreach (var item in accounts)
 | 
			
		||||
            {
 | 
			
		||||
                //删除账号
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_OPENAPIUSERACCOUNT + item);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("禁用用户")]
 | 
			
		||||
    public async Task DisableUserAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        var openApiUser = await GetUsertByIdAsync(input);//获取用户信息
 | 
			
		||||
        if (openApiUser != null)
 | 
			
		||||
        {
 | 
			
		||||
            if (await UpdateAsync(it => new OpenApiUser { UserEnable = false }, it => it.Id == input))
 | 
			
		||||
            {
 | 
			
		||||
                await _verificatService.SetOpenApiVerificatIdAsync(input, new());
 | 
			
		||||
                DeleteUserFromCache(input);//从cache删除用户信息
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("编辑用户")]
 | 
			
		||||
    public async Task EditAsync(OpenApiUserEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var exist = await GetUsertByIdAsync(input.Id);//获取用户信息
 | 
			
		||||
        if (exist != null)
 | 
			
		||||
        {
 | 
			
		||||
            var openApiUser = input.Adapt<OpenApiUser>();//实体转换
 | 
			
		||||
            openApiUser.Password = DESCEncryption.Encrypt(openApiUser.Password, DESCKeyConst.DESCKey);
 | 
			
		||||
            if (await Context.Updateable(openApiUser).IgnoreColumns(it =>
 | 
			
		||||
            new
 | 
			
		||||
            {
 | 
			
		||||
                //忽略更新字段
 | 
			
		||||
                it.LastLoginDevice,
 | 
			
		||||
                it.LastLoginIp,
 | 
			
		||||
                it.LastLoginTime,
 | 
			
		||||
                it.LatestLoginDevice,
 | 
			
		||||
                it.LatestLoginIp,
 | 
			
		||||
                it.LatestLoginTime
 | 
			
		||||
            }).ExecuteCommandAsync() > 0)//修改数据
 | 
			
		||||
                DeleteUserFromCache(openApiUser.Id);//用户缓存到cache
 | 
			
		||||
        }
 | 
			
		||||
        //编辑操作可能会修改用户密码等信息,认证时需要实时获取用户并验证
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("启用用户")]
 | 
			
		||||
    public async Task EnableUserAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        //设置状态为启用
 | 
			
		||||
        if (await UpdateAsync(it => new OpenApiUser { UserEnable = true }, it => it.Id == input))
 | 
			
		||||
            DeleteUserFromCache(input);//从cache删除用户信息
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<long> GetIdByAccountAsync(string account)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿
 | 
			
		||||
        var userId = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<long>(CacheConst.CACHE_OPENAPIUSERACCOUNT + account, false);
 | 
			
		||||
        if (userId == 0)
 | 
			
		||||
        {
 | 
			
		||||
            //单查获取用户账号对应ID
 | 
			
		||||
            userId = await GetFirstAsync(it => it.Account == account, it => it.Id);
 | 
			
		||||
            if (userId != 0)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSERACCOUNT + account, userId, false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return userId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<OpenApiUser> GetUserByAccountAsync(string account)
 | 
			
		||||
    {
 | 
			
		||||
        var userId = await GetIdByAccountAsync(account);//获取用户ID
 | 
			
		||||
        if (userId > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var openApiUser = await GetUsertByIdAsync(userId);//获取用户信息
 | 
			
		||||
            if (openApiUser.Account == account)//这里做了比较用来限制大小写
 | 
			
		||||
                return openApiUser;
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<OpenApiUser> GetUsertByIdAsync(long Id)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var openApiUser = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<OpenApiUser>(CacheConst.CACHE_OPENAPIUSER + Id.ToString(), true);
 | 
			
		||||
        if (openApiUser == null)
 | 
			
		||||
        {
 | 
			
		||||
            openApiUser = await Context.Queryable<OpenApiUser>()
 | 
			
		||||
            .Where(u => u.Id == Id)
 | 
			
		||||
            .FirstAsync();
 | 
			
		||||
            if (openApiUser != null)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_OPENAPIUSER + openApiUser.Id.ToString(), openApiUser, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return openApiUser;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("用户授权")]
 | 
			
		||||
    public async Task GrantRoleAsync(OpenApiUserGrantPermissionInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var openApiUser = await GetUsertByIdAsync(input.Id.Value);//获取用户信息
 | 
			
		||||
        if (openApiUser != null)
 | 
			
		||||
        {
 | 
			
		||||
            openApiUser.PermissionCodeList = input.PermissionList;
 | 
			
		||||
            await CheckInputAsync(openApiUser);
 | 
			
		||||
            if (await Context.Updateable(openApiUser).IgnoreColumns(it =>
 | 
			
		||||
           new
 | 
			
		||||
           {
 | 
			
		||||
               //忽略更新字段
 | 
			
		||||
               it.Password,
 | 
			
		||||
               it.LastLoginDevice,
 | 
			
		||||
               it.LastLoginIp,
 | 
			
		||||
               it.LastLoginTime,
 | 
			
		||||
               it.LatestLoginDevice,
 | 
			
		||||
               it.LatestLoginIp,
 | 
			
		||||
               it.LatestLoginTime
 | 
			
		||||
           }).ExecuteCommandAsync() > 0)//修改数据
 | 
			
		||||
                DeleteUserFromCache(input.Id.Value);//从cache删除用户信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<string>> OwnPermissionsAsync(BaseIdInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var openApiUser = await GetUsertByIdAsync(input.Id);//获取用户信息
 | 
			
		||||
        return openApiUser.PermissionCodeList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<OpenApiUser>()
 | 
			
		||||
         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => u.Account.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
        query = query.OrderBy(it => it.SortCode);//排序
 | 
			
		||||
        query = query.OrderBy(u => u.Id);//排序
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="openApiUser"></param>
 | 
			
		||||
    private async Task CheckInputAsync(OpenApiUser openApiUser)
 | 
			
		||||
    {
 | 
			
		||||
        //判断账号重复,直接从cache拿
 | 
			
		||||
        var account_Id = await GetIdByAccountAsync(openApiUser.Account);
 | 
			
		||||
        if (account_Id > 0 && account_Id != openApiUser.Id)
 | 
			
		||||
            throw Oops.Bah($"存在重复的账号:{openApiUser.Account}");
 | 
			
		||||
        //如果手机号不是空
 | 
			
		||||
        if (!string.IsNullOrEmpty(openApiUser.Phone))
 | 
			
		||||
        {
 | 
			
		||||
            if (!openApiUser.Phone.MatchPhoneNumber())//验证手机格式
 | 
			
		||||
                throw Oops.Bah($"手机号码:{openApiUser.Phone} 格式错误");
 | 
			
		||||
            openApiUser.Phone = DESCEncryption.Encrypt(openApiUser.Phone, DESCKeyConst.DESCKey);
 | 
			
		||||
        }
 | 
			
		||||
        //如果邮箱不是空
 | 
			
		||||
        if (!string.IsNullOrEmpty(openApiUser.Email))
 | 
			
		||||
        {
 | 
			
		||||
            var ismatch = openApiUser.Email.MatchEmail();//验证邮箱格式
 | 
			
		||||
            if (!ismatch)
 | 
			
		||||
                throw Oops.Bah($"邮箱:{openApiUser.Email} 格式错误");
 | 
			
		||||
            if (await IsAnyAsync(it => it.Email == openApiUser.Email && it.Id != openApiUser.Id))
 | 
			
		||||
                throw Oops.Bah($"存在重复的邮箱:{openApiUser.Email}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
{
 | 
			
		||||
  "RECORDS": [
 | 
			
		||||
    {
 | 
			
		||||
      "Id": 444657867911429,
 | 
			
		||||
      "Category": "SYS_USER_HAS_ROLE",
 | 
			
		||||
      "ObjectId": 212725263002001,
 | 
			
		||||
      "TargetId": "212725263001001",
 | 
			
		||||
      "ExtJson": null
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": 444657879060741,
 | 
			
		||||
      "Category": "SYS_USER_HAS_ROLE",
 | 
			
		||||
      "ObjectId": 201725263002001,
 | 
			
		||||
      "TargetId": "212725263001002",
 | 
			
		||||
      "ExtJson": null
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,564 @@
 | 
			
		||||
{
 | 
			
		||||
  "RECORDS": [
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100",
 | 
			
		||||
      "Title": "系统首页",
 | 
			
		||||
      "Icon": "mdi-home-account",
 | 
			
		||||
      "Name": "index",
 | 
			
		||||
      "Component": "/index",
 | 
			
		||||
      "Category": "SPA",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "0",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001",
 | 
			
		||||
      "Title": "权限管理",
 | 
			
		||||
      "Icon": "mdi-account-hard-hat",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "0",
 | 
			
		||||
      "SortCode": "4",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-02-26 00:55:23.977",
 | 
			
		||||
      "UpdateUser": "superAdmin",
 | 
			
		||||
      "UpdateUserId": "212725263002001"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001",
 | 
			
		||||
      "Title": "用户管理",
 | 
			
		||||
      "Icon": "mdi-account-edit",
 | 
			
		||||
      "Name": "sysUser",
 | 
			
		||||
      "Component": "/admin/user",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002",
 | 
			
		||||
      "Title": "角色管理",
 | 
			
		||||
      "Icon": "mdi-account-hard-hat",
 | 
			
		||||
      "Name": "sysRole",
 | 
			
		||||
      "Component": "/admin/role",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100001",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001003",
 | 
			
		||||
      "Title": "菜单管理",
 | 
			
		||||
      "Icon": "mdi-menu",
 | 
			
		||||
      "Name": "sysMenu",
 | 
			
		||||
      "Component": "/admin/menu",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100001",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002002",
 | 
			
		||||
      "Title": "访问日志",
 | 
			
		||||
      "Icon": "mdi-account-switch-outline",
 | 
			
		||||
      "Name": "sysVislog",
 | 
			
		||||
      "Component": "/admin/vislog",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100002",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002003",
 | 
			
		||||
      "Title": "操作日志",
 | 
			
		||||
      "Icon": "mdi-database-search-outline",
 | 
			
		||||
      "Name": "sysOplog",
 | 
			
		||||
      "Component": "/admin/oplog",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100002",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002010",
 | 
			
		||||
      "Title": "定时看板",
 | 
			
		||||
      "Icon": "mdi-database-cog-outline",
 | 
			
		||||
      "Name": "schedulePage",
 | 
			
		||||
      "Component": "/schedulePage",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100002",
 | 
			
		||||
      "SortCode": "4",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002",
 | 
			
		||||
      "Title": "系统运维",
 | 
			
		||||
      "Icon": "mdi-cogs",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "0",
 | 
			
		||||
      "SortCode": "5",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-02-26 00:55:33.503",
 | 
			
		||||
      "UpdateUser": "superAdmin",
 | 
			
		||||
      "UpdateUserId": "212725263002001"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002001",
 | 
			
		||||
      "Title": "系统配置",
 | 
			
		||||
      "Icon": "mdi-cog-transfer-outline",
 | 
			
		||||
      "Name": "sysConfig",
 | 
			
		||||
      "Component": "/admin/config",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003",
 | 
			
		||||
      "Title": "第三方授权",
 | 
			
		||||
      "Icon": "mdi-transit-transfer",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "0",
 | 
			
		||||
      "SortCode": "6",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-02-26 00:55:29.094",
 | 
			
		||||
      "UpdateUser": "superAdmin",
 | 
			
		||||
      "UpdateUserId": "212725263002001"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003001",
 | 
			
		||||
      "Title": "令牌列表",
 | 
			
		||||
      "Icon": "mdi-transit-transfer",
 | 
			
		||||
      "Name": "sysOpenApiSession",
 | 
			
		||||
      "Component": "/admin/openapisession",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100003",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002",
 | 
			
		||||
      "Title": "授权用户",
 | 
			
		||||
      "Icon": "mdi-transit-transfer",
 | 
			
		||||
      "Name": "sysOpenApiUser",
 | 
			
		||||
      "Component": "/admin/openapiuser",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100003",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003003",
 | 
			
		||||
      "Title": "接口文档",
 | 
			
		||||
      "Icon": "mdi-cog-transfer-outline",
 | 
			
		||||
      "Name": "swaggerUrl",
 | 
			
		||||
      "Component": "/api/index.html",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100003",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "BLANK",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002004",
 | 
			
		||||
      "Title": "会话管理",
 | 
			
		||||
      "Icon": "mdi-transit-transfer",
 | 
			
		||||
      "Name": "session",
 | 
			
		||||
      "Component": "/admin/session",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100002",
 | 
			
		||||
      "SortCode": "4",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysuseradd",
 | 
			
		||||
      "ParentId": "100001001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001004",
 | 
			
		||||
      "Title": "单页管理",
 | 
			
		||||
      "Icon": "mdi-menu",
 | 
			
		||||
      "Name": "sysSpa",
 | 
			
		||||
      "Component": "/admin/spa",
 | 
			
		||||
      "Category": "MENU",
 | 
			
		||||
      "Code": "system",
 | 
			
		||||
      "ParentId": "100001",
 | 
			
		||||
      "SortCode": "4",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysuseredit",
 | 
			
		||||
      "ParentId": "100001001",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysuserdelete",
 | 
			
		||||
      "ParentId": "100001001",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysroleadd",
 | 
			
		||||
      "ParentId": "100001002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysroleedit",
 | 
			
		||||
      "ParentId": "100001002",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysroledelete",
 | 
			
		||||
      "ParentId": "100001002",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001003001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysmenuadd",
 | 
			
		||||
      "ParentId": "100001003",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001003002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysmenuedit",
 | 
			
		||||
      "ParentId": "100001003",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001003003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysmenudelete",
 | 
			
		||||
      "ParentId": "100001003",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001004001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysspaadd",
 | 
			
		||||
      "ParentId": "100001004",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001004002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysspaedit",
 | 
			
		||||
      "ParentId": "100001004",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001004003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysspadelete",
 | 
			
		||||
      "ParentId": "100001004",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002001001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysconfigadd",
 | 
			
		||||
      "ParentId": "100002001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002001002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysconfigedit",
 | 
			
		||||
      "ParentId": "100002001",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002001003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysconfigdelete",
 | 
			
		||||
      "ParentId": "100002001",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002001",
 | 
			
		||||
      "Title": "添加",
 | 
			
		||||
      "Name": "add",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiuseradd",
 | 
			
		||||
      "ParentId": "100003002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002002",
 | 
			
		||||
      "Title": "编辑",
 | 
			
		||||
      "Name": "edit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiuseredit",
 | 
			
		||||
      "ParentId": "100003002",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002003",
 | 
			
		||||
      "Title": "删除",
 | 
			
		||||
      "Name": "delete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiuserdelete",
 | 
			
		||||
      "ParentId": "100003002",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001004",
 | 
			
		||||
      "Title": "重置密码",
 | 
			
		||||
      "Name": "resetpassword",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysuserresetpassword",
 | 
			
		||||
      "ParentId": "100001001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001001005",
 | 
			
		||||
      "Title": "角色授权",
 | 
			
		||||
      "Name": "perrole",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysuserperrole",
 | 
			
		||||
      "ParentId": "100001001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002004",
 | 
			
		||||
      "Title": "资源授权",
 | 
			
		||||
      "Name": "perresuorce",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysroleperresuorce",
 | 
			
		||||
      "ParentId": "100001002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100001002005",
 | 
			
		||||
      "Title": "用户授权",
 | 
			
		||||
      "Name": "peruser",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysroleperuser",
 | 
			
		||||
      "ParentId": "100001002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002004004",
 | 
			
		||||
      "Title": "会话强退",
 | 
			
		||||
      "Name": "exit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "syssessionexit",
 | 
			
		||||
      "ParentId": "100002004",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002004005",
 | 
			
		||||
      "Title": "令牌删除",
 | 
			
		||||
      "Name": "verificatdelete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysverificatdelete",
 | 
			
		||||
      "ParentId": "100002004",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003001004",
 | 
			
		||||
      "Title": "会话强退",
 | 
			
		||||
      "Name": "exit",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapisessionexit",
 | 
			
		||||
      "ParentId": "100003001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003001005",
 | 
			
		||||
      "Title": "令牌删除",
 | 
			
		||||
      "Name": "verificatdelete",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiverificatdelete",
 | 
			
		||||
      "ParentId": "100003001",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002002003",
 | 
			
		||||
      "Title": "清空",
 | 
			
		||||
      "Name": "clear",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysoplogclear",
 | 
			
		||||
      "ParentId": "100002002",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100002003003",
 | 
			
		||||
      "Title": "清空",
 | 
			
		||||
      "Name": "clear",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "sysvislogclear",
 | 
			
		||||
      "ParentId": "100002003",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002004",
 | 
			
		||||
      "Title": "修改密码",
 | 
			
		||||
      "Name": "editpassword",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiusereditpassword",
 | 
			
		||||
      "ParentId": "100003002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "100003002005",
 | 
			
		||||
      "Title": "授权Api",
 | 
			
		||||
      "Name": "per",
 | 
			
		||||
      "Category": "BUTTON",
 | 
			
		||||
      "Code": "openapiuserper",
 | 
			
		||||
      "ParentId": "100003002",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "TargetType": "None",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "391545543004421",
 | 
			
		||||
      "Title": "个人中心",
 | 
			
		||||
      "Icon": "mdi-home-account",
 | 
			
		||||
      "Component": "/usercenter",
 | 
			
		||||
      "Category": "SPA",
 | 
			
		||||
      "Code": "391545542885637",
 | 
			
		||||
      "ParentId": "0",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "TargetType": "SELF",
 | 
			
		||||
      "CreateTime": "2023-03-02 19:42:55.6049703",
 | 
			
		||||
      "CreateUser": "superAdmin",
 | 
			
		||||
      "CreateUserId": "212725263002001",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-03-02 19:46:13.3919053",
 | 
			
		||||
      "UpdateUser": "superAdmin",
 | 
			
		||||
      "UpdateUserId": "212725263002001"
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,145 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 系统配置种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysConfig> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        List<SysConfig> configList = new List<SysConfig>
 | 
			
		||||
        {
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222222,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_SWAGGER_NAME,
 | 
			
		||||
                ConfigValue = "admin",
 | 
			
		||||
                Remark = "swagger账号",
 | 
			
		||||
                SortCode = 1,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222223,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_SWAGGER_PASSWORD,
 | 
			
		||||
                ConfigValue = "123456",
 | 
			
		||||
                Remark = "swagger密码",
 | 
			
		||||
                SortCode = 2,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222224,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_SWAGGERLOGIN_OPEN,
 | 
			
		||||
                ConfigValue = "false",
 | 
			
		||||
                Remark = "swagger开启登录",
 | 
			
		||||
                SortCode = 3,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222226,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_TITLE,
 | 
			
		||||
                ConfigValue = "ThingsGateway",
 | 
			
		||||
                Remark = "标题",
 | 
			
		||||
                SortCode = 5,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222228,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_COPYRIGHT,
 | 
			
		||||
                ConfigValue = "ThingsGateway ©2023 Diego",
 | 
			
		||||
                Remark = "系统版权",
 | 
			
		||||
                SortCode = 6,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222299,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_COPYRIGHT_URL,
 | 
			
		||||
                ConfigValue = "https://gitee.com/diego2098/ThingsGateway",
 | 
			
		||||
                Remark = "系统版权链接地址",
 | 
			
		||||
                SortCode = 7,
 | 
			
		||||
            },
 | 
			
		||||
                        new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222229,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_PAGETAB,
 | 
			
		||||
                ConfigValue = "true",
 | 
			
		||||
                Remark = "开启标签页",
 | 
			
		||||
                SortCode = 7,
 | 
			
		||||
            },
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222231,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_PASSWORD,
 | 
			
		||||
                ConfigValue = "111111",
 | 
			
		||||
                Remark = "默认用户密码",
 | 
			
		||||
                SortCode = 8,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222227,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_VERIFICAT_EXPIRES,
 | 
			
		||||
                ConfigValue = "14400",
 | 
			
		||||
                Remark = "Verificat过期时间(分)",
 | 
			
		||||
                SortCode = 9,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222232,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_SINGLE_OPEN,
 | 
			
		||||
                ConfigValue = "false",
 | 
			
		||||
                Remark = "单用户登录开关",
 | 
			
		||||
                SortCode = 10,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222230,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_CAPTCHA_OPEN,
 | 
			
		||||
                ConfigValue = "true",
 | 
			
		||||
                Remark = "登录验证码开关",
 | 
			
		||||
                SortCode = 11,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysConfig
 | 
			
		||||
            {
 | 
			
		||||
                Id = 22222222222225,
 | 
			
		||||
                Category = ConfigConst.SYS_CONFIGBASEDEFAULT,
 | 
			
		||||
                ConfigKey = ConfigConst.CONFIG_REMARK,
 | 
			
		||||
                ConfigValue = "边缘采集网关",
 | 
			
		||||
                Remark = "说明",
 | 
			
		||||
                SortCode = 12,
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        return configList;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 关系表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysRelation> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysRelation>("sys_relation.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,25 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 资源表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysResourceSeedData : ISqlSugarEntitySeedData<SysResource>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysResource> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysResource>("sys_resource.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysRoleSeedData : ISqlSugarEntitySeedData<SysRole>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysRole> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        List<SysRole> configList = new List<SysRole>
 | 
			
		||||
        {
 | 
			
		||||
            new SysRole
 | 
			
		||||
            {
 | 
			
		||||
                Id = 212725263001001,
 | 
			
		||||
                Code = RoleConst.SuperAdmin,
 | 
			
		||||
                Name = "超级管理员",
 | 
			
		||||
                SortCode = 1,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysRole
 | 
			
		||||
             {
 | 
			
		||||
                Id = 212725263001002,
 | 
			
		||||
                Code = "admin",
 | 
			
		||||
                Name = "业务管理员",
 | 
			
		||||
                SortCode = 2,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        return configList;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysUser> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        List<SysUser> configList = new List<SysUser>
 | 
			
		||||
        {
 | 
			
		||||
            new SysUser
 | 
			
		||||
            {
 | 
			
		||||
                Id = 212725263002001,
 | 
			
		||||
                Account = RoleConst.SuperAdmin,
 | 
			
		||||
                Password = "111111",
 | 
			
		||||
                UserEnable = true,
 | 
			
		||||
                SortCode = 1,
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            new SysUser
 | 
			
		||||
             {
 | 
			
		||||
                Id = 201725263002001,
 | 
			
		||||
                Account = "admin",
 | 
			
		||||
                Password = "111111",
 | 
			
		||||
                UserEnable = true,
 | 
			
		||||
                SortCode = 2,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        return configList;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,26 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 即时通讯集线器
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISysHub
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 退出登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task Logout(object context);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,112 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.InstantMessaging;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Http.Connections.Features;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="ISysHub"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[MapHub(HubConst.SysHubUrl)]
 | 
			
		||||
public class SysHub : Hub<ISysHub>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分隔符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string Separate = "_s_e_p_a_r_a_t_e_";
 | 
			
		||||
 | 
			
		||||
    private readonly ILogger<ISysHub> _logger;
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    /// <inheritdoc cref="ISysHub"/>
 | 
			
		||||
    public SysHub(IServiceScopeFactory scopeFactory, ILogger<ISysHub> logger)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = scopeFactory.CreateScope();
 | 
			
		||||
        _logger = logger;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override async Task OnConnectedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var feature = Context.Features.Get<IHttpContextFeature>();
 | 
			
		||||
        var VerificatId = feature.HttpContext.Request.Headers[ClaimConst.VerificatId].FirstOrDefault().ToLong();
 | 
			
		||||
 | 
			
		||||
        var userIdentifier = Context.UserIdentifier;//自定义的Id
 | 
			
		||||
        await UpdateVerificatAsync(userIdentifier, verificat: VerificatId);//更新cache
 | 
			
		||||
        await base.OnConnectedAsync();
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 断开连接
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="exception"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public override async Task OnDisconnectedAsync(Exception exception)
 | 
			
		||||
    {
 | 
			
		||||
        var userIdentifier = Context.UserIdentifier;//自定义的Id
 | 
			
		||||
        await UpdateVerificatAsync(userIdentifier, false);//更新cache
 | 
			
		||||
        await base.OnDisconnectedAsync(exception);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新cache
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userIdentifier">用户id</param>
 | 
			
		||||
    /// <param name="verificat">上线时的验证id</param>
 | 
			
		||||
    /// <param name="isConnect">是否是上线</param>
 | 
			
		||||
    private async Task UpdateVerificatAsync(string userIdentifier, bool isConnect = true, long verificat = 0)
 | 
			
		||||
    {
 | 
			
		||||
        var userId = userIdentifier.Split(Separate)[0].ToLong();//分割取第一个
 | 
			
		||||
        if (userId > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var _verificatService = _serviceScope.ServiceProvider.GetService<IVerificatService>();
 | 
			
		||||
            //获取cache当前用户的verificat信息列表
 | 
			
		||||
            List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(userId);
 | 
			
		||||
 | 
			
		||||
            if (verificatInfos != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (isConnect)
 | 
			
		||||
                {
 | 
			
		||||
                    //获取cache中当前verificat
 | 
			
		||||
                    var verificatInfo = verificatInfos.Where(it => it.Id == verificat).FirstOrDefault();
 | 
			
		||||
                    if (verificatInfo != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        verificatInfo.ClientIds.Add(userIdentifier);//添加到客户端列表
 | 
			
		||||
                        await _verificatService.SetVerificatIdAsync(userId, verificatInfos);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //获取当前客户端ID所在的verificat信息
 | 
			
		||||
                    var verificatInfo = verificatInfos.Where(it => it.ClientIds.Contains(userIdentifier)).FirstOrDefault();
 | 
			
		||||
                    if (verificatInfo != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        verificatInfo.ClientIds.RemoveWhere(it => it == userIdentifier);//从客户端列表删除
 | 
			
		||||
                        await _verificatService.SetVerificatIdAsync(userId, verificatInfos);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Http.Connections.Features;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户ID提供器
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserIdProvider : IUserIdProvider
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public string GetUserId(HubConnectionContext connection)
 | 
			
		||||
    {
 | 
			
		||||
        var feature = connection.Features.Get<IHttpContextFeature>();
 | 
			
		||||
        var UserId = feature.HttpContext.Request.Headers[ClaimConst.UserId].FirstOrDefault()?.ToLong();
 | 
			
		||||
 | 
			
		||||
        if (UserId > 0)
 | 
			
		||||
        {
 | 
			
		||||
            return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return connection.ConnectionId;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								framework/admin/ThingsGateway.Admin.Application/Startup.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,39 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// AppStartup启动类
 | 
			
		||||
/// </summary>
 | 
			
		||||
[AppStartup(9997)]
 | 
			
		||||
public class Startup : AppStartup
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void ConfigureServices(IServiceCollection services)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        // 任务调度
 | 
			
		||||
        services.AddSchedule(options =>
 | 
			
		||||
        {
 | 
			
		||||
            options.AddPersistence<JobPersistence>();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //事件总线
 | 
			
		||||
        services.AddEventBus();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 认证模块事件总线
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class AuthEventSubscriber : IEventSubscriber, ISingleton
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录事件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [EventSubscribe(EventSubscriberConst.Login)]
 | 
			
		||||
    public async Task Login(EventHandlerExecutingContext context)
 | 
			
		||||
    {
 | 
			
		||||
        var loginEvent = (LoginEvent)context.Source.Payload;//获取参数
 | 
			
		||||
        var sysUser = loginEvent.SysUser;
 | 
			
		||||
        var db = DbContext.Db.CopyNew();
 | 
			
		||||
 | 
			
		||||
        #region 重新赋值属性,设置本次登录信息为最新的信息
 | 
			
		||||
 | 
			
		||||
        db.Tracking(sysUser);//创建跟踪,只更新修改字段
 | 
			
		||||
        sysUser.LastLoginDevice = sysUser.LatestLoginDevice;
 | 
			
		||||
        sysUser.LastLoginIp = sysUser.LatestLoginIp;
 | 
			
		||||
        sysUser.LastLoginTime = sysUser.LatestLoginTime;
 | 
			
		||||
        sysUser.LatestLoginDevice = loginEvent.Device.ToString();
 | 
			
		||||
        sysUser.LatestLoginIp = loginEvent.Ip;
 | 
			
		||||
        sysUser.LatestLoginTime = loginEvent.DateTime;
 | 
			
		||||
 | 
			
		||||
        #endregion 重新赋值属性,设置本次登录信息为最新的信息
 | 
			
		||||
 | 
			
		||||
        //更新用户信息
 | 
			
		||||
        if (await db.UpdateableWithAttr(sysUser).ExecuteCommandAsync() > 0)
 | 
			
		||||
        {
 | 
			
		||||
            App.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSUSER + sysUser.Id, sysUser, false); //更新Cache信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户模块事件总线
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserEventSubscriber : IEventSubscriber, ISingleton
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceProvider _services;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="UserEventSubscriber"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="services"></param>
 | 
			
		||||
    public UserEventSubscriber(IServiceProvider services)
 | 
			
		||||
    {
 | 
			
		||||
        _services = services;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据角色ID列表清除用户缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [EventSubscribe(EventSubscriberConst.ClearUserCache)]
 | 
			
		||||
    public async Task DeleteUserCacheByRoleIds(EventHandlerExecutingContext context)
 | 
			
		||||
    {
 | 
			
		||||
        var roleIds = (List<long>)context.Source.Payload;//获取角色ID
 | 
			
		||||
        // 创建新的作用域
 | 
			
		||||
        using var scope = _services.CreateScope();
 | 
			
		||||
        // 解析角色服务
 | 
			
		||||
        var relationService = scope.ServiceProvider.GetRequiredService<IRelationService>();
 | 
			
		||||
        //获取用户和角色关系
 | 
			
		||||
        var relations = await relationService.GetRelationListByTargetIdListAndCategoryAsync(roleIds.Select(it => it.ToString()).ToList(), CateGoryConst.Relation_SYS_USER_HAS_ROLE);
 | 
			
		||||
        if (relations.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var userIds = relations.Select(it => it.ObjectId).ToArray();//用户ID列表
 | 
			
		||||
            // 解析用户服务
 | 
			
		||||
            var userService = scope.ServiceProvider.GetRequiredService<ISysUserService>();
 | 
			
		||||
            //从缓存中删除
 | 
			
		||||
            userService.DeleteUserFromCache(userIds);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,240 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DataEncryption;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Authentication.Cookies;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IAuthService"/>
 | 
			
		||||
public class AuthService : IAuthService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IConfigService _configService;
 | 
			
		||||
    private readonly IEventPublisher _eventPublisher;
 | 
			
		||||
    private readonly INoticeService _noticeService;
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    private readonly ISysUserService _userService;
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
    /// <inheritdoc cref="IAuthService"/>
 | 
			
		||||
    public AuthService(
 | 
			
		||||
                       IEventPublisher eventPublisher,
 | 
			
		||||
                       ISysUserService userService,
 | 
			
		||||
                       IConfigService configService,
 | 
			
		||||
                       IVerificatService verificatService,
 | 
			
		||||
                        INoticeService noticeService, IServiceScopeFactory serviceScopeFactory
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _eventPublisher = eventPublisher;
 | 
			
		||||
        _userService = userService;
 | 
			
		||||
        _configService = configService;
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
        _noticeService = noticeService;
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ValidCodeOutput GetCaptchaInfo()
 | 
			
		||||
    {
 | 
			
		||||
        //生成验证码
 | 
			
		||||
        var captchInfo = new Random().Next(1111, 9999).ToString();
 | 
			
		||||
        //生成请求号,并将验证码放入cache
 | 
			
		||||
        var reqNo = YitIdHelper.NextId();
 | 
			
		||||
        //插入cache
 | 
			
		||||
        _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.LOGIN_CAPTCHA + reqNo, captchInfo, TimeSpan.FromMinutes(1), false);
 | 
			
		||||
        //返回验证码和请求号
 | 
			
		||||
        return new ValidCodeOutput { CodeValue = captchInfo, ValidCodeReqNo = reqNo };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SysUser> GetLoginUserAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return await _userService.GetUserByIdAsync(UserManager.UserId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<LoginOutput> LoginAsync(LoginInput input)
 | 
			
		||||
    {
 | 
			
		||||
        //判断是否有验证码
 | 
			
		||||
        var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_CAPTCHA_OPEN);
 | 
			
		||||
 | 
			
		||||
        if (sysBase != null)//如果有这个配置项
 | 
			
		||||
        {
 | 
			
		||||
            if (sysBase.ConfigValue.ToBool(false))//如果需要验证码
 | 
			
		||||
            {
 | 
			
		||||
                //如果没填验证码,提示验证码不能为空
 | 
			
		||||
                if (input.ValidCode.IsNullOrEmpty() || input.ValidCodeReqNo == 0) throw Oops.Bah("验证码不能为空").StatusCode(410);
 | 
			
		||||
                ValidValidCode(input.ValidCode, input.ValidCodeReqNo);//校验验证码
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var password = DESCEncryption.Decrypt(input.Password, DESCKeyConst.DESCKey);  // 解密
 | 
			
		||||
        var userInfo = await _userService.GetUserByAccountAsync(input.Account) ?? throw Oops.Bah("用户不存在");//获取用户信息
 | 
			
		||||
        if (userInfo.Password != password) throw Oops.Bah("账号密码错误");//账号密码错误
 | 
			
		||||
        return await LoginAsync(userInfo, input.Device);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task LogoutAsync()
 | 
			
		||||
    {
 | 
			
		||||
        //获取用户信息
 | 
			
		||||
        var userinfo = await _userService.GetUserByAccountAsync(UserManager.UserAccount);
 | 
			
		||||
        if (userinfo != null)
 | 
			
		||||
        {
 | 
			
		||||
            LoginEvent loginEvent = new()
 | 
			
		||||
            {
 | 
			
		||||
                Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
 | 
			
		||||
                SysUser = userinfo,
 | 
			
		||||
                VerificatId = UserManager.VerificatId.ToLong(),
 | 
			
		||||
            };
 | 
			
		||||
            await RemoveVerificatAsync(loginEvent);//移除验证Id
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 校验验证码方法
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="validCode">验证码</param>
 | 
			
		||||
    /// <param name="validCodeReqNo">请求号</param>
 | 
			
		||||
    /// <param name="isDelete">是否从Cache删除</param>
 | 
			
		||||
    private void ValidValidCode(string validCode, long validCodeReqNo, bool isDelete = true)
 | 
			
		||||
    {
 | 
			
		||||
        var code = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<string>(CacheConst.LOGIN_CAPTCHA + validCodeReqNo, false);//从cache拿数据
 | 
			
		||||
        if (isDelete) _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.LOGIN_CAPTCHA + validCodeReqNo);//删除验证码
 | 
			
		||||
        if (code != null)//如果有
 | 
			
		||||
        {
 | 
			
		||||
            //验证码如果不匹配直接抛错误,这里忽略大小写
 | 
			
		||||
            if (validCode.ToLower() != code.ToLower()) throw Oops.Bah("验证码错误");
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Bah("验证码已过期");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 执行B端登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysUser">用户信息</param>
 | 
			
		||||
    /// <param name="device">登录设备</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    private async Task<LoginOutput> LoginAsync(SysUser sysUser, AuthDeviceTypeEnum device)
 | 
			
		||||
    {
 | 
			
		||||
        if (sysUser.UserEnable == false) throw Oops.Bah("账号已停用");//账号已停用
 | 
			
		||||
 | 
			
		||||
        var sysBase = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_VERIFICAT_EXPIRES);
 | 
			
		||||
        var sessionid = YitIdHelper.NextId();
 | 
			
		||||
        var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
 | 
			
		||||
        identity.AddClaim(new Claim(ClaimConst.VerificatId, sessionid.ToString()));
 | 
			
		||||
        identity.AddClaim(new Claim(ClaimConst.UserId, sysUser.Id.ToString()));
 | 
			
		||||
        identity.AddClaim(new Claim(ClaimConst.Account, sysUser.Account));
 | 
			
		||||
        identity.AddClaim(new Claim(ClaimConst.IsSuperAdmin, sysUser.RoleCodeList.Contains(RoleConst.SuperAdmin).ToString()));
 | 
			
		||||
        identity.AddClaim(new Claim(ClaimConst.IsOpenApi, false.ToString()));
 | 
			
		||||
 | 
			
		||||
        var config = sysBase.ConfigValue.ToInt(2880);
 | 
			
		||||
        var diffTime = DateTimeExtensions.CurrentDateTime.AddMinutes(config);
 | 
			
		||||
        await App.HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties()
 | 
			
		||||
        {
 | 
			
		||||
            IsPersistent = true,
 | 
			
		||||
            ExpiresUtc = diffTime,
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //登录事件参数
 | 
			
		||||
        var loginEvent = new LoginEvent
 | 
			
		||||
        {
 | 
			
		||||
            Ip = App.HttpContext.GetRemoteIpAddressToIPv4(),
 | 
			
		||||
            Device = device,
 | 
			
		||||
            Expire = config,
 | 
			
		||||
            SysUser = sysUser,
 | 
			
		||||
            VerificatId = sessionid,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        await SetVerificatAsync(loginEvent);//写入verificat
 | 
			
		||||
 | 
			
		||||
        await _eventPublisher.PublishAsync(EventSubscriberConst.Login, loginEvent); //发布登录事件总线
 | 
			
		||||
        return new LoginOutput { VerificatId = sessionid, Account = sysUser.Account };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private async Task RemoveVerificatAsync(LoginEvent loginEvent)
 | 
			
		||||
    {
 | 
			
		||||
        //获取verificat列表
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id);
 | 
			
		||||
        if (verificatInfos != null)
 | 
			
		||||
        {
 | 
			
		||||
            //获取当前用户的verificat
 | 
			
		||||
            var verificat = verificatInfos.Where(it => it.Id == loginEvent.VerificatId).FirstOrDefault();
 | 
			
		||||
            if (verificat != null)
 | 
			
		||||
                verificatInfos.Remove(verificat);
 | 
			
		||||
            //更新verificat列表
 | 
			
		||||
            await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos);
 | 
			
		||||
        }
 | 
			
		||||
        await App.HttpContext?.SignOutAsync();
 | 
			
		||||
        App.HttpContext?.SignoutToSwagger();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 写入验证信息到缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="loginEvent"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    private async Task SetVerificatAsync(LoginEvent loginEvent)
 | 
			
		||||
    {
 | 
			
		||||
        //获取verificat列表
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(loginEvent.SysUser.Id);
 | 
			
		||||
        var verificatTimeout = loginEvent.DateTime.AddMinutes(loginEvent.Expire);
 | 
			
		||||
        //生成verificat信息
 | 
			
		||||
        var verificatInfo = new VerificatInfo
 | 
			
		||||
        {
 | 
			
		||||
            Device = loginEvent.Device.ToString(),
 | 
			
		||||
            Expire = loginEvent.Expire,
 | 
			
		||||
            VerificatTimeout = verificatTimeout,
 | 
			
		||||
            Id = loginEvent.VerificatId,
 | 
			
		||||
            UserId = loginEvent.SysUser.Id,
 | 
			
		||||
        };
 | 
			
		||||
        if (verificatInfos != null)
 | 
			
		||||
        {
 | 
			
		||||
            bool isSingle = false;//默认不开启单用户登录
 | 
			
		||||
 | 
			
		||||
            var singleConfig = await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_SINGLE_OPEN);//获取系统单用户登录选项
 | 
			
		||||
            if (singleConfig != null) isSingle = singleConfig.ConfigValue.ToBool(false);//如果配置不为空则设置单用户登录选项为系统配置的值
 | 
			
		||||
            if (isSingle)//判断是否单用户登录
 | 
			
		||||
            {
 | 
			
		||||
                await _noticeService.LogoutAsync(loginEvent.SysUser.Id, verificatInfos.Where(it => it.Device == loginEvent.Device.ToString()).ToList(), "该账号已在别处登录!");//通知其他用户下线
 | 
			
		||||
                verificatInfos = verificatInfos.Where(it => it.Device != loginEvent.Device.ToString()).ToList();//去掉当前登录类型
 | 
			
		||||
                verificatInfos.Add(verificatInfo);//添加到列表
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                verificatInfos.Add(verificatInfo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            verificatInfos = new List<VerificatInfo> { verificatInfo };//直接就一个
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //添加到verificat列表
 | 
			
		||||
        await _verificatService.SetVerificatIdAsync(loginEvent.SysUser.Id, verificatInfos);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,78 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录输入参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginInput : ValidCodeInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设备类型,默认PC
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <example>0</example>
 | 
			
		||||
    public AuthDeviceTypeEnum Device { get; set; } = AuthDeviceTypeEnum.PC;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 密码
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Required(ErrorMessage = "密码不能为空"), MinLength(3, ErrorMessage = "密码不能少于3个字符")]
 | 
			
		||||
    public string Password { get; set; }
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 验证码输入
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ValidCodeInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string ValidCode { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 请求号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long ValidCodeReqNo { get; set; }
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录设备类型枚举
 | 
			
		||||
/// </summary>
 | 
			
		||||
public enum AuthDeviceTypeEnum
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// PC端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("PC端")]
 | 
			
		||||
    PC,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 移动端
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("移动端")]
 | 
			
		||||
    APP,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Api
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("Api")]
 | 
			
		||||
    Api,
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录返回参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class BaseLoginOutput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long VerificatId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 验证码值返回
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ValidCodeOutput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证码值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string CodeValue { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证码请求号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long ValidCodeReqNo { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录返回参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginOutput : BaseLoginOutput
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录事件参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginEvent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime DateTime = DateTimeExtensions.CurrentDateTime;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录设备
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public AuthDeviceTypeEnum Device { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 过期时间(分)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public int Expire { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Ip地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Ip { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public SysUser SysUser { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long VerificatId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,44 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 权限校验服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IAuthService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 生成验证码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    ValidCodeOutput GetCaptchaInfo();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取登录用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<SysUser> GetLoginUserAsync();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<LoginOutput> LoginAsync(LoginInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 退出登录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task LogoutAsync();
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,163 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IButtonService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class ButtonService : DbRepository<SysResource>, IButtonService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IRelationService _relationService;
 | 
			
		||||
    private readonly IResourceService _resourceService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IButtonService"/>
 | 
			
		||||
    public ButtonService(
 | 
			
		||||
        IResourceService resourceService,
 | 
			
		||||
        IRelationService relationService
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _resourceService = resourceService;
 | 
			
		||||
        _relationService = relationService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task AddAsync(ButtonAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
        if (await InsertAsync(sysResource))//插入数据
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("删除按钮")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有ID
 | 
			
		||||
        var ids = input.ToList();
 | 
			
		||||
        //获取所有按钮集合
 | 
			
		||||
        var buttonList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.BUTTON);
 | 
			
		||||
 | 
			
		||||
        #region 处理关系表角色资源信息
 | 
			
		||||
 | 
			
		||||
        //获取所有菜单集合
 | 
			
		||||
        var menuList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
 | 
			
		||||
        //获取按钮的父菜单id集合
 | 
			
		||||
        var parentIds = buttonList.Where(it => ids.Contains(it.Id)).Select(it => it.ParentId.ToString()).ToList();
 | 
			
		||||
        //获取关系表分类为SYS_ROLE_HAS_RESOURCE数据
 | 
			
		||||
        var roleResources = await _relationService.GetRelationByCategoryAsync(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);
 | 
			
		||||
        //获取相关关系表数据
 | 
			
		||||
        var relationList = roleResources
 | 
			
		||||
                .Where(it => parentIds.Contains(it.TargetId))//目标ID是父ID中
 | 
			
		||||
                .Where(it => it.ExtJson != null).ToList();//扩展信息不为空
 | 
			
		||||
 | 
			
		||||
        //遍历关系表
 | 
			
		||||
        relationList.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            var relationRoleResuorce = it.ExtJson.FromJsonString<RelationRoleResuorce>();//拓展信息转实体
 | 
			
		||||
            var buttonInfo = relationRoleResuorce.ButtonInfo;//获取按钮信息
 | 
			
		||||
            if (buttonInfo.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var diffArr = buttonInfo.Where(it => !buttonInfo.Contains(it)).ToList(); //找出不同的元素(即交集的补集)
 | 
			
		||||
                relationRoleResuorce.ButtonInfo = diffArr;//重新赋值按钮信息
 | 
			
		||||
                it.ExtJson = relationRoleResuorce.ToJsonString();//重新赋值拓展信息
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        #endregion 处理关系表角色资源信息
 | 
			
		||||
 | 
			
		||||
        //事务
 | 
			
		||||
        var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除按钮
 | 
			
		||||
            if (relationList.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                await Context.Updateable(relationList).UpdateColumns(it => it.ExtJson).ExecuteCommandAsync();//修改拓展信息
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        if (result.IsSuccess)//如果成功了
 | 
			
		||||
        {
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("编辑按钮")]
 | 
			
		||||
    public async Task EditAsync(ButtonEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
                                                     //事务
 | 
			
		||||
        var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            await UpdateAsync(sysResource); //更新按钮
 | 
			
		||||
        });
 | 
			
		||||
        if (result.IsSuccess)//如果成功了
 | 
			
		||||
        {
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysResource>()
 | 
			
		||||
                         .Where(it => it.ParentId == input.ParentId && it.Category == ResourceCategoryEnum.BUTTON)
 | 
			
		||||
                         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title.Contains(input.SearchKey) || it.Component.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
        query = query.OrderBy(it => it.SortCode);//排序
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysResource"></param>
 | 
			
		||||
    private async Task CheckInputAsync(SysResource sysResource)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有按钮和菜单
 | 
			
		||||
        var buttonList = await _resourceService.GetListByCategorysAsync(new List<ResourceCategoryEnum> { ResourceCategoryEnum.BUTTON, ResourceCategoryEnum.MENU });
 | 
			
		||||
        //判断code是否重复
 | 
			
		||||
        if (buttonList.Any(it => it.Code == sysResource.Code && it.Id != sysResource.Id))
 | 
			
		||||
            throw Oops.Bah($"存在重复的按钮编码:{sysResource.Code}");
 | 
			
		||||
        //判断菜单是否存在
 | 
			
		||||
        if (!buttonList.Any(it => it.Id == sysResource.ParentId))
 | 
			
		||||
            throw Oops.Bah($"不存在的父级菜单:{sysResource.ParentId}");
 | 
			
		||||
        sysResource.Category = ResourceCategoryEnum.BUTTON;//设置分类为按钮
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,61 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 添加按钮参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ButtonAddInput : SysResource
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Code不能为空")]
 | 
			
		||||
    public override string Code { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "ParentId不能为空")]
 | 
			
		||||
    public override long ParentId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 标题
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Title不能为空")]
 | 
			
		||||
    public override string Title { get; set; }
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 按钮分页
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ButtonPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "ParentId不能为空")]
 | 
			
		||||
    public long? ParentId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 按钮编辑
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ButtonEditInput : ButtonAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,51 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 权限按钮服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IButtonService : ITransient
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加按钮
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(ButtonAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除按钮
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">删除参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑按钮
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">编辑参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(ButtonEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 按钮分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询条件</param>
 | 
			
		||||
    /// <returns>按钮分页列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,137 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IConfigService"/>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class ConfigService : DbRepository<SysConfig>, IConfigService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    public ConfigService(IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("编辑网关系统配置")]
 | 
			
		||||
    public async Task EditBatchAsync(List<SysConfig> sysConfigs)
 | 
			
		||||
    {
 | 
			
		||||
        if (await UpdateRangeAsync(sysConfigs))
 | 
			
		||||
            RefreshCache(sysConfigs.FirstOrDefault()?.Category);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("添加配置项")]
 | 
			
		||||
    public async Task AddAsync(ConfigAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查
 | 
			
		||||
        var sysConfig = input.Adapt<SysConfig>();//实体转换
 | 
			
		||||
        if (await InsertAsync(sysConfig))//插入数据)
 | 
			
		||||
            RefreshCache(input.Category);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("删除配置项")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] input)
 | 
			
		||||
    {
 | 
			
		||||
        await AsDeleteable().Where(it => input.Contains(it.Id)).ExecuteCommandAsync();
 | 
			
		||||
        RefreshCache(ConfigConst.SYS_CONFIGOTHER);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("编辑配置项")]
 | 
			
		||||
    public async Task EditAsync(ConfigEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);
 | 
			
		||||
        var sysConfig = input.Adapt<SysConfig>();//实体转换
 | 
			
		||||
        if (await UpdateAsync(sysConfig))//更新数据
 | 
			
		||||
            RefreshCache(input.Category);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SysConfig> GetByConfigKeyAsync(string category, string configKey)
 | 
			
		||||
    {
 | 
			
		||||
        var configList = await GetListByCategoryAsync(category);//获取系统配置列表
 | 
			
		||||
        return configList.FirstOrDefault(it => it.ConfigKey == configKey);//根据configkey获取对应值
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysConfig>> GetListByCategoryAsync(string category)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var configList = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysConfig>>(CacheConst.SYS_CONFIGCATEGORY + category, true);
 | 
			
		||||
        if (configList == null)
 | 
			
		||||
        {
 | 
			
		||||
            //cache没有再去数据可拿
 | 
			
		||||
            configList = await Context.Queryable<SysConfig>().Where(it => it.Category == category).OrderBy(it => it.SortCode).ToListAsync();//获取系统配置列表
 | 
			
		||||
            if (configList.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.SYS_CONFIGCATEGORY + category, configList, true);//如果不为空,插入cache
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return configList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<SysConfig>> PageAsync(ConfigPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysConfig>()
 | 
			
		||||
                         .Where(it => it.Category == ConfigConst.SYS_CONFIGOTHER)//自定义配置
 | 
			
		||||
                         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.ConfigKey.Contains(input.SearchKey) || it.ConfigKey.Contains(input.SearchKey));
 | 
			
		||||
        //根据关键字查询
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
        query = query.OrderBy(it => it.SortCode);//排序
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数,并设置分类为自定义分类
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysConfig"></param>
 | 
			
		||||
    private async Task CheckInputAsync(SysConfig sysConfig)
 | 
			
		||||
    {
 | 
			
		||||
        var configs = await GetListByCategoryAsync(sysConfig.Category);//获取全部字典
 | 
			
		||||
        var hasSameKey = configs.Any(it => it.ConfigKey == sysConfig.ConfigKey && it.Id != sysConfig.Id);
 | 
			
		||||
        //判断是否从存在重复字典名
 | 
			
		||||
        if (hasSameKey)
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Bah($"存在重复的配置键:{sysConfig.ConfigKey}");
 | 
			
		||||
        }
 | 
			
		||||
        sysConfig.Category = ConfigConst.SYS_CONFIGOTHER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 刷新缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    private void RefreshCache(string category)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.SYS_CONFIGCATEGORY + category);//cache删除
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,65 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 添加配置参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ConfigAddInput : SysConfig
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分类
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Category不能为空")]
 | 
			
		||||
    public override string Category { get; set; } = ConfigConst.SYS_CONFIGOTHER;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置键
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "configKey不能为空")]
 | 
			
		||||
    public override string ConfigKey { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置值
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "ConfigValue不能为空")]
 | 
			
		||||
    public override string ConfigValue { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 编辑配置参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ConfigEditInput : ConfigAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 配置分页参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ConfigPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分类
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("分类")]
 | 
			
		||||
    public string Category { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 系统配置服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IConfigService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量编辑系统配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="configs">配置列表</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditBatchAsync(List<SysConfig> configs);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 新增自定义配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">新增参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(ConfigAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除自定义配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">删除</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 修改自定义配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">修改参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(ConfigEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据分类和配置键获配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <param name="configKey">配置键</param>
 | 
			
		||||
    /// <returns>配置信息</returns>
 | 
			
		||||
    Task<SysConfig> GetByConfigKeyAsync(string category, string configKey);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据分类获取配置列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类名称</param>
 | 
			
		||||
    /// <returns>配置列表</returns>
 | 
			
		||||
    Task<List<SysConfig>> GetListByCategoryAsync(string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分页查询自定义配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>其他配置列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SysConfig>> PageAsync(ConfigPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,38 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Components.Forms;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IFileService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class FileService : IFileService
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void ImportVerification(IBrowserFile file, int maxSzie = 300, string[] allowTypes = null)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        if (file == null) throw Oops.Bah("文件不能为空");
 | 
			
		||||
        if (file.Size > maxSzie * 1024 * 1024) throw Oops.Bah($"文件大小不允许超过{maxSzie}M");
 | 
			
		||||
        var fileSuffix = Path.GetExtension(file.Name).ToLower().Split(".")[1]; // 文件后缀
 | 
			
		||||
        string[] allowTypeS = allowTypes ?? new string[] { "xlsx" };//允许上传的文件类型
 | 
			
		||||
        if (!allowTypeS.Contains(fileSuffix)) throw Oops.Bah(errorMessage: "文件格式错误");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Components.Forms;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 文件管理服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IFileService : ITransient
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证上传文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="file">文件</param>
 | 
			
		||||
    /// <param name="maxSzie">最大体积(M)</param>
 | 
			
		||||
    /// <param name="allowTypes">允许上传类型</param>
 | 
			
		||||
    void ImportVerification(IBrowserFile file, int maxSzie = 30, string[] allowTypes = null);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,91 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 添加菜单参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class MenuAddInput : SysResource, IValidatableObject
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 路径
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override string Component { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 图标
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Icon不能为空")]
 | 
			
		||||
    public override string Icon { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "ParentId不能为空")]
 | 
			
		||||
    public override long ParentId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 菜单类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 标题
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Title不能为空")]
 | 
			
		||||
    public override string Title { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 特殊验证
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="validationContext"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    /// <exception cref="NotImplementedException"></exception>
 | 
			
		||||
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
 | 
			
		||||
    {
 | 
			
		||||
        //如果菜单类型是菜单
 | 
			
		||||
        if (TargetType == TargetTypeEnum.SELF)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(Component))
 | 
			
		||||
                yield return new ValidationResult("路径不能为空", new[] { nameof(Component) });
 | 
			
		||||
        }
 | 
			
		||||
        //设置分类为菜单
 | 
			
		||||
        Category = ResourceCategoryEnum.MENU;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 编辑菜单输入参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class MenuEditInput : MenuAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 菜单树查询参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class MenuPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "ParentId不能为空")]
 | 
			
		||||
    public long ParentId { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 菜单服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IMenuService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加菜单
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(MenuAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除菜单
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">删除菜单参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 详情
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">id</param>
 | 
			
		||||
    /// <returns>详细信息</returns>
 | 
			
		||||
    Task<SysResource> DetailAsync(BaseIdInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑菜单
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">菜单编辑参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(MenuEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据模块获取菜单树
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">菜单树查询参数</param>
 | 
			
		||||
    /// <returns>菜单树列表</returns>
 | 
			
		||||
    Task<List<SysResource>> TreeAsync(MenuPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,166 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IMenuService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class MenuService : DbRepository<SysResource>, IMenuService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IRelationService _relationService;
 | 
			
		||||
    private readonly IResourceService _resourceService;
 | 
			
		||||
    private readonly IRoleService _roleService;
 | 
			
		||||
    /// <inheritdoc cref="IMenuService"/>
 | 
			
		||||
    public MenuService(IResourceService resourceService, IRelationService relationService, IRoleService roleService)
 | 
			
		||||
    {
 | 
			
		||||
        _roleService = roleService;
 | 
			
		||||
        _resourceService = resourceService;
 | 
			
		||||
        _relationService = relationService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("添加菜单")]
 | 
			
		||||
    public async Task AddAsync(MenuAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
 | 
			
		||||
        if (await InsertAsync(sysResource))//插入数据
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.MENU);//刷新菜单缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("删除菜单")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有ID
 | 
			
		||||
        var ids = input.ToList();
 | 
			
		||||
        if (ids.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            //获取所有菜单和按钮
 | 
			
		||||
            var resourceList = await _resourceService.GetListByCategorysAsync(new List<ResourceCategoryEnum> { ResourceCategoryEnum.MENU, ResourceCategoryEnum.BUTTON });
 | 
			
		||||
            //找到要删除的菜单
 | 
			
		||||
            var sysResources = resourceList.Where(it => ids.Contains(it.Id)).ToList();
 | 
			
		||||
            //查找内置菜单
 | 
			
		||||
            var system = sysResources.Where(it => it.Code == ResourceConst.System).FirstOrDefault();
 | 
			
		||||
            if (system != null)
 | 
			
		||||
                throw Oops.Bah($"不可删除系统菜单:{system.Title}");
 | 
			
		||||
            //需要删除的资源ID列表
 | 
			
		||||
            var resourceIds = new List<long>();
 | 
			
		||||
            //遍历菜单列表
 | 
			
		||||
            sysResources.ForEach(it =>
 | 
			
		||||
            {
 | 
			
		||||
                //获取菜单所有子节点
 | 
			
		||||
                var child = _resourceService.GetChildListById(resourceList, it.Id, false);
 | 
			
		||||
                //将子节点ID添加到删除资源ID列表
 | 
			
		||||
                resourceIds.AddRange(child.Select(it => it.Id).ToList());
 | 
			
		||||
                resourceIds.Add(it.Id);//添加到删除资源ID列表
 | 
			
		||||
            });
 | 
			
		||||
            ids.AddRange(resourceIds);//添加到删除ID列表
 | 
			
		||||
                                      //事务
 | 
			
		||||
            var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除菜单和按钮
 | 
			
		||||
                await Context.Deleteable<SysRelation>()//关系表删除对应SYS_ROLE_HAS_RESOURCE
 | 
			
		||||
                 .Where(it => it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE && resourceIds.Contains(SqlFunc.ToInt64(it.TargetId))).ExecuteCommandAsync();
 | 
			
		||||
            });
 | 
			
		||||
            if (result.IsSuccess)//如果成功了
 | 
			
		||||
            {
 | 
			
		||||
                _resourceService.RefreshCache(ResourceCategoryEnum.MENU);//资源表菜单刷新缓存
 | 
			
		||||
                _resourceService.RefreshCache(ResourceCategoryEnum.BUTTON);//资源表按钮刷新缓存
 | 
			
		||||
                _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//关系表刷新缓存
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                //写日志
 | 
			
		||||
                throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<SysResource> DetailAsync(BaseIdInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var sysResources = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
 | 
			
		||||
        var resource = sysResources.Where(it => it.Id == input.Id).FirstOrDefault();
 | 
			
		||||
        return resource;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("编辑菜单")]
 | 
			
		||||
    public async Task EditAsync(MenuEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
        if (await UpdateAsync(sysResource))//更新数据
 | 
			
		||||
        {
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.MENU);//刷新菜单缓存
 | 
			
		||||
            //需要更新资源权限,因为地址可能改变,页面权限需要更改
 | 
			
		||||
            await _roleService.RefreshResourceAsync(input.Id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<List<SysResource>> TreeAsync(MenuPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有菜单
 | 
			
		||||
        var sysResources = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
 | 
			
		||||
        sysResources = sysResources
 | 
			
		||||
            .Where(it => it.ParentId == input.ParentId)
 | 
			
		||||
            .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title == input.SearchKey)//根据关键字查找
 | 
			
		||||
            .ToList();
 | 
			
		||||
        //构建菜单树
 | 
			
		||||
        var tree = _resourceService.ResourceListToTree(sysResources, input.ParentId);
 | 
			
		||||
        return tree;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysResource"></param>
 | 
			
		||||
    private async Task CheckInputAsync(SysResource sysResource)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有菜单列表
 | 
			
		||||
        var menList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
 | 
			
		||||
        //判断是否有同级且同名的菜单
 | 
			
		||||
        if (menList.Any(it => it.ParentId == sysResource.ParentId && it.Title == sysResource.Title && it.Id != sysResource.Id))
 | 
			
		||||
            throw Oops.Bah($"存在重复的菜单名称:{sysResource.Title}");
 | 
			
		||||
        if (sysResource.ParentId != 0)
 | 
			
		||||
        {
 | 
			
		||||
            //获取父级,判断父级ID正不正确
 | 
			
		||||
            var parent = menList.Where(it => it.Id == sysResource.ParentId).FirstOrDefault();
 | 
			
		||||
            if (parent != null)
 | 
			
		||||
            {
 | 
			
		||||
                if (parent.Id == sysResource.Id)
 | 
			
		||||
                    throw Oops.Bah($"上级菜单不能选择自己");
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                throw Oops.Bah($"上级菜单不存在:{sysResource.ParentId}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 通知服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface INoticeService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通知用户下线
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">用户ID</param>
 | 
			
		||||
    /// <param name="verificatInfos">验证列表</param>
 | 
			
		||||
    /// <param name="message">消息内容</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,43 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="INoticeService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class NoticeService : INoticeService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    public NoticeService(IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message)
 | 
			
		||||
    {
 | 
			
		||||
        //客户端ID列表
 | 
			
		||||
        var clientIds = new List<string>();
 | 
			
		||||
        //遍历cancellationToken列表获取客户端ID列表
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            clientIds.AddRange(it.ClientIds);
 | 
			
		||||
        });
 | 
			
		||||
        //获取signalr实例
 | 
			
		||||
        var signalr = _serviceScope.ServiceProvider.GetService<IHubContext<SysHub, ISysHub>>();
 | 
			
		||||
        //发送其他客户端登录消息
 | 
			
		||||
        await signalr.Clients.Users(clientIds).Logout(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 操作日志分页输入
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OperateLogPageInput : VisitLogPageInput
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 操作日志分页输入
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class OperateLogInput : VisitLogInput
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,47 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 操作日志服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IOperateLogService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据分类删除操作日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类名称</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params string[] category);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 导出后台日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<MemoryStream> ExportFileAsync(List<SysOperateLog> input = null);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 导出后台日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<MemoryStream> ExportFileAsync(OperateLogInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 操作日志分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>分页列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SysOperateLog>> PageAsync(OperateLogPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,105 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using MiniExcelLibs;
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IOperateLogService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class OperateLogService : DbRepository<SysOperateLog>, IOperateLogService
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("删除操作日志")]
 | 
			
		||||
    public async Task DeleteAsync(params string[] category)
 | 
			
		||||
    {
 | 
			
		||||
        await AsDeleteable().Where(it => category.Contains(it.Category)).ExecuteCommandAsync();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("导出操作日志", IsRecordPar = false)]
 | 
			
		||||
    public async Task<MemoryStream> ExportFileAsync(List<SysOperateLog> input = null)
 | 
			
		||||
    {
 | 
			
		||||
        input ??= await GetListAsync();
 | 
			
		||||
 | 
			
		||||
        //总数据
 | 
			
		||||
        Dictionary<string, object> sheets = new();
 | 
			
		||||
        List<Dictionary<string, object>> devExports = new();
 | 
			
		||||
        foreach (var devData in input)
 | 
			
		||||
        {
 | 
			
		||||
            #region sheet
 | 
			
		||||
            //变量页
 | 
			
		||||
            var data = devData.GetType().GetProperties();
 | 
			
		||||
            Dictionary<string, object> devExport = new();
 | 
			
		||||
            foreach (var item in data)
 | 
			
		||||
            {
 | 
			
		||||
                //描述
 | 
			
		||||
                var desc = item.FindDisplayAttribute();
 | 
			
		||||
                //数据源增加
 | 
			
		||||
                devExport.Add(desc ?? item.Name, item.GetValue(devData)?.ToString());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            devExports.Add(devExport);
 | 
			
		||||
 | 
			
		||||
            #endregion
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sheets.Add("操作日志", devExports);
 | 
			
		||||
 | 
			
		||||
        var memoryStream = new MemoryStream();
 | 
			
		||||
        await memoryStream.SaveAsAsync(sheets);
 | 
			
		||||
        memoryStream.Seek(0, SeekOrigin.Begin);
 | 
			
		||||
        return memoryStream;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("导出操作日志", IsRecordPar = false)]
 | 
			
		||||
    public async Task<MemoryStream> ExportFileAsync(OperateLogInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = GetPage(input.Adapt<OperateLogPageInput>());
 | 
			
		||||
        var data = await query.ToListAsync();
 | 
			
		||||
        return await ExportFileAsync(data);
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<ISqlSugarPagedList<SysOperateLog>> PageAsync(OperateLogPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = GetPage(input);
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private ISugarQueryable<SysOperateLog> GetPage(OperateLogPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysOperateLog>()
 | 
			
		||||
                             .WhereIF(input.StartTime != null, a => a.OpTime >= input.StartTime.Value.ToLocalTime())
 | 
			
		||||
                           .WhereIF(input.EndTime != null, a => a.OpTime <= input.EndTime.Value.ToLocalTime())
 | 
			
		||||
                           .WhereIF(!string.IsNullOrEmpty(input.Account), it => it.OpAccount == input.Account)//根据账号查询
 | 
			
		||||
                           .WhereIF(!string.IsNullOrEmpty(input.Category), it => it.Category == input.Category)//根据分类查询
 | 
			
		||||
                           .WhereIF(!string.IsNullOrEmpty(input.ExeStatus), it => it.ExeStatus == input.ExeStatus)//根据结果查询
 | 
			
		||||
                           .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey) || it.OpIp.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
        query = query.OrderBy(it => it.Id, OrderByType.Desc);//排序
 | 
			
		||||
        return query;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,97 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 关系服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IRelationService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取关系表用户工作台
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">用户ID</param>
 | 
			
		||||
    /// <returns>关系表数据</returns>
 | 
			
		||||
    Task<SysRelation> GetWorkbenchAsync(long userId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据分类获取关系表信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类名称</param>
 | 
			
		||||
    /// <returns>关系表</returns>
 | 
			
		||||
    Task<List<SysRelation>> GetRelationByCategoryAsync(string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过对象ID和分类获取关系列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="objectId">对象ID</param>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysRelation>> GetRelationListByObjectIdAndCategoryAsync(long objectId, string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过对象ID列表和分类获取关系列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="objectIds">对象ID</param>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysRelation>> GetRelationListByObjectIdListAndCategoryAsync(List<long> objectIds, string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过目标ID和分类获取关系列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="targetId">目标ID</param>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysRelation>> GetRelationListByTargetIdAndCategoryAsync(string targetId, string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 通过目标ID列表和分类获取关系列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="targetIds"></param>
 | 
			
		||||
    /// <param name="category"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysRelation>> GetRelationListByTargetIdListAndCategoryAsync(List<string> targetIds, string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    void RefreshCache(string category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 保存关系
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <param name="objectId">对象ID</param>
 | 
			
		||||
    /// <param name="targetId">目标ID</param>
 | 
			
		||||
    /// <param name="extJson">拓展信息</param>
 | 
			
		||||
    /// <param name="clear">是否清除老的数据</param>
 | 
			
		||||
    /// <param name="refreshCache">是否刷新缓存</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task SaveRelationAsync(string category, long objectId, string targetId, string extJson, bool clear, bool refreshCache = true);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 批量保存关系
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <param name="objectId">对象ID</param>
 | 
			
		||||
    /// <param name="targetIds">目标ID列表</param>
 | 
			
		||||
    /// <param name="extJsons">拓展信息列表</param>
 | 
			
		||||
    /// <param name="clear">是否清除老的数据</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task SaveRelationBatchAsync(string category, long objectId, List<string> targetIds, List<string> extJsons, bool clear);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,150 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IRelationService"/>
 | 
			
		||||
public class RelationService : DbRepository<SysRelation>, IRelationService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    public RelationService(IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationByCategoryAsync(string category)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var sysRelations = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysRelation>>(CacheConst.CACHE_SYSRELATION + category, true);
 | 
			
		||||
        if (sysRelations == null)
 | 
			
		||||
        {
 | 
			
		||||
            //cache没有就去数据库拿
 | 
			
		||||
            sysRelations = await GetListAsync(it => it.Category == category);
 | 
			
		||||
            if (sysRelations.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSRELATION + category, sysRelations, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return sysRelations;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationListByObjectIdAndCategoryAsync(long objectId, string category)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = await GetRelationByCategoryAsync(category);
 | 
			
		||||
        var result = sysRelations.Where(it => it.ObjectId == objectId).ToList();//获取关系集合
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationListByObjectIdListAndCategoryAsync(List<long> objectIds, string category)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = await GetRelationByCategoryAsync(category);
 | 
			
		||||
        var result = sysRelations.Where(it => objectIds.Contains(it.ObjectId)).ToList();//获取关系集合
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationListByTargetIdAndCategoryAsync(string targetId, string category)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = await GetRelationByCategoryAsync(category);
 | 
			
		||||
        var result = sysRelations.Where(it => it.TargetId == targetId).ToList();//获取关系集合
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationListByTargetIdListAndCategoryAsync(List<string> targetIds, string category)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = await GetRelationByCategoryAsync(category);
 | 
			
		||||
        var result = sysRelations.Where(it => targetIds.Contains(it.TargetId)).ToList();//获取关系集合
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SysRelation> GetWorkbenchAsync(long userId)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = await GetRelationByCategoryAsync(CateGoryConst.Relation_SYS_USER_WORKBENCH_DATA);
 | 
			
		||||
        var result = sysRelations.FirstOrDefault(it => it.ObjectId == userId);//获取个人工作台
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void RefreshCache(string category)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRELATION + category);//删除cache
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task SaveRelationAsync(string category, long objectId, string targetId, string extJson, bool clear, bool refreshCache = true)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelation = new SysRelation
 | 
			
		||||
        {
 | 
			
		||||
            ObjectId = objectId,
 | 
			
		||||
            TargetId = targetId,
 | 
			
		||||
            Category = category,
 | 
			
		||||
            ExtJson = extJson
 | 
			
		||||
        };
 | 
			
		||||
        //事务
 | 
			
		||||
        var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            if (clear)
 | 
			
		||||
                await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
 | 
			
		||||
            await InsertAsync(sysRelation);//添加新的
 | 
			
		||||
        });
 | 
			
		||||
        if (result.IsSuccess)//如果成功了
 | 
			
		||||
        {
 | 
			
		||||
            if (refreshCache)
 | 
			
		||||
                RefreshCache(category);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            //写日志
 | 
			
		||||
            throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task SaveRelationBatchAsync(string category, long objectId, List<string> targetIds, List<string> extJsons, bool clear)
 | 
			
		||||
    {
 | 
			
		||||
        var sysRelations = new List<SysRelation>();//要添加的列表
 | 
			
		||||
        for (int i = 0; i < targetIds.Count; i++)
 | 
			
		||||
        {
 | 
			
		||||
            sysRelations.Add(new SysRelation
 | 
			
		||||
            {
 | 
			
		||||
                ObjectId = objectId,
 | 
			
		||||
                TargetId = targetIds[i],
 | 
			
		||||
                Category = category,
 | 
			
		||||
                ExtJson = extJsons?[i]
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
        {
 | 
			
		||||
            if (clear)
 | 
			
		||||
                await DeleteAsync(it => it.ObjectId == objectId && it.Category == category);//删除老的
 | 
			
		||||
            await InsertRangeAsync(sysRelations);//添加新的
 | 
			
		||||
        });
 | 
			
		||||
        if (result.IsSuccess)//如果成功了
 | 
			
		||||
        {
 | 
			
		||||
            RefreshCache(category);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色按钮资源
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RoleGrantResourceButton
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 按钮id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 标题
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Title { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 授权菜单类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RoleGrantResourceMenu
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 菜单下按钮集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public List<RoleGrantResourceButton> Button { get; set; } = new List<RoleGrantResourceButton>();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 菜单id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long ParentId { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 父名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string ParentName { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 菜单名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Title { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Blazor Server的组件路由内容
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PermissionTreeSelector
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 路由名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string ApiRoute { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,98 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 资源服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IResourceService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取所有的菜单和模块以及单页面列表,并按分类和排序码排序,不会形成树列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns>所有的菜单和模块以及单页面列表</returns>
 | 
			
		||||
    Task<List<SysResource>> GetaMenuAndSpaListAsync();
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取子资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysResources"></param>
 | 
			
		||||
    /// <param name="resId"></param>
 | 
			
		||||
    /// <param name="isContainOneself"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    List<SysResource> GetChildListById(List<SysResource> sysResources, long resId, bool isContainOneself = true);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取ID获取Code列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="ids">id列表</param>
 | 
			
		||||
    /// <param name="category">分类</param>
 | 
			
		||||
    /// <returns>Code列表</returns>
 | 
			
		||||
    Task<List<string>> GetCodeByIdsAsync(List<long> ids, ResourceCategoryEnum category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据分类获取资源列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类名称</param>
 | 
			
		||||
    /// <returns>资源列表</returns>
 | 
			
		||||
    Task<List<SysResource>> GetListByCategoryAsync(ResourceCategoryEnum category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 资源分类列表,如果是空的则获取全部资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="categorys">资源分类列表</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysResource>> GetListByCategorysAsync(List<ResourceCategoryEnum> categorys = null);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取资源所有下级
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="resourceList">资源列表</param>
 | 
			
		||||
    /// <param name="parentId">父ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    List<SysResource> GetResourceChilden(List<SysResource> resourceList, long parentId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取上级
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    List<SysResource> GetResourceParent(List<SysResource> resourceList, long parentId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取授权菜单
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<RoleGrantResourceMenu>> GetRoleGrantResourceMenusAsync();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 刷新缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="category">分类名称</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    void RefreshCache(ResourceCategoryEnum category);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 构建菜单树形结构
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="resourceList">菜单列表</param>
 | 
			
		||||
    /// <param name="parentId">父ID</param>
 | 
			
		||||
    /// <returns>菜单形结构</returns>
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    List<SysResource> ResourceListToTree(List<SysResource> resourceList, long parentId = 0);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 多个树转列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="data"></param>
 | 
			
		||||
    List<SysResource> ResourceTreeToList(List<SysResource> data);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,269 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IResourceService"/>
 | 
			
		||||
public class ResourceService : DbRepository<SysResource>, IResourceService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IServiceScope _serviceScope;
 | 
			
		||||
    public ResourceService(IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysResource>> GetaMenuAndSpaListAsync()
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有的菜单以及单页面
 | 
			
		||||
        var sysResources = await GetListByCategorysAsync((List<ResourceCategoryEnum>)new() { ResourceCategoryEnum.MENU, ResourceCategoryEnum.SPA });
 | 
			
		||||
        return sysResources?.OrderBy(it => it.Category).ThenBy(it => it.SortCode).ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public List<SysResource> GetChildListById(List<SysResource> sysResources, long resId, bool isContainOneself = true)
 | 
			
		||||
    {
 | 
			
		||||
        //查找下级
 | 
			
		||||
        var childLsit = GetResourceChilden(sysResources, resId);
 | 
			
		||||
        if (isContainOneself)//如果包含自己
 | 
			
		||||
        {
 | 
			
		||||
            //获取自己
 | 
			
		||||
            var self = sysResources.Where(it => it.Id == resId).FirstOrDefault();
 | 
			
		||||
            if (self != null) childLsit.Insert(0, self);//如果不为空就插到第一个
 | 
			
		||||
        }
 | 
			
		||||
        return childLsit;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<List<string>> GetCodeByIdsAsync(List<long> ids, ResourceCategoryEnum category)
 | 
			
		||||
    {
 | 
			
		||||
        //根据分类获取所有
 | 
			
		||||
        var sysResources = await GetListByCategoryAsync(category);
 | 
			
		||||
        return sysResources.Where(it => ids.Contains(it.Id)).Select(it => it.Code).ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public async Task<List<SysResource>> GetListByCategoryAsync(ResourceCategoryEnum category)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var sysResources = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysResource>>(CacheConst.CACHE_SYSRESOURCE + category.ToString(), true);
 | 
			
		||||
        if (sysResources == null)
 | 
			
		||||
        {
 | 
			
		||||
            //cache没有就去数据库拿
 | 
			
		||||
            sysResources = await GetListAsync(it => it.Category == category);
 | 
			
		||||
            if (sysResources.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSRESOURCE + category.ToString(), sysResources, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return sysResources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysResource>> GetListByCategorysAsync(List<ResourceCategoryEnum> categoryList = null)
 | 
			
		||||
    {
 | 
			
		||||
        //定义结果
 | 
			
		||||
        var sysResources = new List<SysResource>();
 | 
			
		||||
 | 
			
		||||
        //定义资源分类列表,如果是空的则获取全部资源
 | 
			
		||||
        categoryList = categoryList != null ? categoryList
 | 
			
		||||
            : new List<ResourceCategoryEnum> { ResourceCategoryEnum.MENU, ResourceCategoryEnum.BUTTON, ResourceCategoryEnum.SPA };
 | 
			
		||||
        //遍历列表
 | 
			
		||||
        foreach (var category in categoryList)
 | 
			
		||||
        {
 | 
			
		||||
            //根据分类获取到资源列表
 | 
			
		||||
            var data = await GetListByCategoryAsync(category);
 | 
			
		||||
            //添加到结果集
 | 
			
		||||
            sysResources.AddRange(data);
 | 
			
		||||
        }
 | 
			
		||||
        return sysResources;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public List<SysResource> GetResourceChilden(List<SysResource> resourceList, long parentId)
 | 
			
		||||
    {
 | 
			
		||||
        //找下级资源ID列表
 | 
			
		||||
        var resources = resourceList.Where(it => it.ParentId == parentId).ToList();
 | 
			
		||||
        if (resources.Count > 0)//如果数量大于0
 | 
			
		||||
        {
 | 
			
		||||
            var data = new List<SysResource>();
 | 
			
		||||
            foreach (var item in resources)//遍历资源
 | 
			
		||||
            {
 | 
			
		||||
                var res = GetResourceChilden(resourceList, item.Id);
 | 
			
		||||
                data.AddRange(res);//添加子节点;
 | 
			
		||||
                data.Add(item);//添加到列表
 | 
			
		||||
            }
 | 
			
		||||
            return data;//返回结果
 | 
			
		||||
        }
 | 
			
		||||
        return new List<SysResource>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public List<SysResource> GetResourceParent(List<SysResource> resourceList, long parentId)
 | 
			
		||||
    {
 | 
			
		||||
        //找上级资源ID列表
 | 
			
		||||
        var resources = resourceList.Where(it => it.Id == parentId).FirstOrDefault();
 | 
			
		||||
        if (resources != null)//如果数量大于0
 | 
			
		||||
        {
 | 
			
		||||
            var data = new List<SysResource>();
 | 
			
		||||
            var parents = GetResourceParent(resourceList, resources.ParentId);
 | 
			
		||||
            data.AddRange(parents);//添加子节点;
 | 
			
		||||
            data.Add(resources);//添加到列表
 | 
			
		||||
            return data;//返回结果
 | 
			
		||||
        }
 | 
			
		||||
        return new List<SysResource>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<RoleGrantResourceMenu>> GetRoleGrantResourceMenusAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var roleGrantResourceMenus = new List<RoleGrantResourceMenu>();//定义结果
 | 
			
		||||
        List<SysResource> allMenuList = (await GetListByCategoryAsync(ResourceCategoryEnum.MENU));//获取所有菜单列表
 | 
			
		||||
        List<SysResource> allButtonList = await GetListByCategoryAsync(ResourceCategoryEnum.BUTTON);//获取所有按钮列表
 | 
			
		||||
        var parentMenuList = allMenuList.Where(it => it.ParentId == 0).ToList();//获取一级目录
 | 
			
		||||
 | 
			
		||||
        //遍历一级目录
 | 
			
		||||
        foreach (var parent in parentMenuList)
 | 
			
		||||
        {
 | 
			
		||||
            //如果是目录则去遍历下级
 | 
			
		||||
            if (parent.TargetType == TargetTypeEnum.None)
 | 
			
		||||
            {
 | 
			
		||||
                //获取所有下级菜单
 | 
			
		||||
                var menuList = GetChildListById(allMenuList, parent.Id, false);
 | 
			
		||||
 | 
			
		||||
                //遍历下级菜单
 | 
			
		||||
                foreach (var menu in menuList)
 | 
			
		||||
                {
 | 
			
		||||
                    //如果菜单类型是菜单
 | 
			
		||||
                    if (menu.TargetType == TargetTypeEnum.SELF)
 | 
			
		||||
                    {
 | 
			
		||||
                        //获取菜单下按钮集合并转换成对应实体
 | 
			
		||||
                        var buttonList = allButtonList.Where(it => it.ParentId == menu.Id).ToList();
 | 
			
		||||
                        var buttons = buttonList.Adapt<List<RoleGrantResourceButton>>();
 | 
			
		||||
                        roleGrantResourceMenus.Add(new()
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = menu.Id,
 | 
			
		||||
                            ParentId = parent.Id,
 | 
			
		||||
                            ParentName = parent.Title,
 | 
			
		||||
                            Title = GetRoleGrantResourceMenuTitle(parentMenuList, menu),//菜单名称需要特殊处理因为有二级菜单
 | 
			
		||||
                            Button = buttons
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                    else if (menu.TargetType == TargetTypeEnum.BLANK || menu.TargetType == TargetTypeEnum.CALLBACK)//如果是内链或者外链
 | 
			
		||||
                    {
 | 
			
		||||
                        //直接加到资源列表
 | 
			
		||||
                        roleGrantResourceMenus.Add(new()
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = menu.Id,
 | 
			
		||||
                            ParentId = parent.Id,
 | 
			
		||||
                            ParentName = parent.Title,
 | 
			
		||||
                            Title = menu.Title,
 | 
			
		||||
                        });
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                //否则就将自己加到一级目录里面
 | 
			
		||||
                roleGrantResourceMenus.Add(new()
 | 
			
		||||
                {
 | 
			
		||||
                    Id = parent.Id,
 | 
			
		||||
                    ParentId = parent.Id,
 | 
			
		||||
                    ParentName = parent.Title,
 | 
			
		||||
                    Title = parent.Title,
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return roleGrantResourceMenus;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void RefreshCache(ResourceCategoryEnum category)
 | 
			
		||||
    {
 | 
			
		||||
        //如果分类是空的
 | 
			
		||||
        if (category == ResourceCategoryEnum.None)
 | 
			
		||||
        {
 | 
			
		||||
            //删除全部key
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.SPA.ToString());
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.BUTTON.ToString());
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.MENU.ToString());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            //否则只删除一个Key
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSRESOURCE + category.ToString());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public List<SysResource> ResourceListToTree(List<SysResource> resourceList, long parentId = 0)
 | 
			
		||||
    {
 | 
			
		||||
        //找下级资源ID列表
 | 
			
		||||
        var resources = resourceList
 | 
			
		||||
           .Where(it => it.ParentId == parentId).OrderBy(it => it.SortCode).ToList();
 | 
			
		||||
        if (resources.Count > 0)//如果数量大于0
 | 
			
		||||
        {
 | 
			
		||||
            var data = new List<SysResource>();
 | 
			
		||||
            foreach (var item in resources)//遍历资源
 | 
			
		||||
            {
 | 
			
		||||
                var children = ResourceListToTree(resourceList, item.Id);//添加子节点
 | 
			
		||||
                item.Children = children.Count > 0 ? children : null;
 | 
			
		||||
                data.Add(item);//添加到列表
 | 
			
		||||
            }
 | 
			
		||||
            return data;//返回结果
 | 
			
		||||
        }
 | 
			
		||||
        return new List<SysResource>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public List<SysResource> ResourceTreeToList(List<SysResource> data)
 | 
			
		||||
    {
 | 
			
		||||
        List<SysResource> list = new();
 | 
			
		||||
        foreach (var item in data)
 | 
			
		||||
        {
 | 
			
		||||
            list.Add(item);
 | 
			
		||||
            if (item.Children != null && item.Children.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                list.AddRange(ResourceTreeToList(item.Children));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取授权菜单类菜单名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="menuList">菜单列表</param>
 | 
			
		||||
    /// <param name="menu">当前菜单</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    private string GetRoleGrantResourceMenuTitle(List<SysResource> menuList, SysResource menu)
 | 
			
		||||
    {
 | 
			
		||||
        //查找菜单上级
 | 
			
		||||
        var parentList = GetResourceParent(menuList, menu.ParentId);
 | 
			
		||||
        //如果有父级菜单
 | 
			
		||||
        if (parentList.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var titles = parentList.Select(it => it.Title).ToList();//提取出父级的name
 | 
			
		||||
            var title = $"{string.Join("- ", titles)}-{menu.Title}";//根据-分割,转换成字符串并在最后加上菜单的title
 | 
			
		||||
            return title;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return menu.Title;//原路返回
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,82 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色授权资源参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class GrantResourceInput : RoleOwnResourceOutput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 授权资源信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "授权资源信息表不能为空")]
 | 
			
		||||
    public override List<RelationRoleResuorce> GrantInfoList { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色授权用户参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class GrantUserInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 授权权限信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "GrantInfoList不能为空")]
 | 
			
		||||
    public List<long> GrantInfoList { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public long? Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色添加参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RoleAddInput : SysRole
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 名称
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Name不能为空")]
 | 
			
		||||
    public override string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色编辑参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RoleEditInput : RoleAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色查询参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class RolePageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,105 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IRoleService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(RoleAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">删除参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">编辑角色</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(RoleEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据用户ID获取用户角色Id集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">用户ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<long>> GetRoleIdListByUserIdAsync(long userId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据用户ID获取用户角色集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId">用户ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysRole>> GetRoleListByUserIdAsync(long userId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 给角色授权资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">授权参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task GrantResourceAsync(GrantResourceInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 给角色授权用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">授权信息</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task GrantUserAsync(GrantUserInput input);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色拥有资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">角色id</param>
 | 
			
		||||
    /// <returns>角色拥有资源信息</returns>
 | 
			
		||||
    Task<RoleOwnResourceOutput> OwnResourceAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取角色下的用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">角色ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<long>> OwnUserAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分页查询角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SysRole>> PageAsync(RolePageInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 刷新缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    void RefreshCache();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色刷新资源
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task RefreshResourceAsync(long? menuId = null);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 角色选择器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<List<SysRole>> RoleSelectorAsync(string searchKey = null);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,410 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Foundation.Extension.String;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="IRoleService"/>
 | 
			
		||||
    [Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
    public class RoleService : DbRepository<SysRole>, IRoleService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IEventPublisher _eventPublisher;
 | 
			
		||||
        private readonly IRelationService _relationService;
 | 
			
		||||
        private readonly IResourceService _resourceService;
 | 
			
		||||
        private readonly IServiceScope _serviceScope;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc cref="IRoleService"/>
 | 
			
		||||
        public RoleService(
 | 
			
		||||
                           IRelationService relationService,
 | 
			
		||||
                           IResourceService resourceService,
 | 
			
		||||
                           IEventPublisher eventPublisher,
 | 
			
		||||
                           IServiceScopeFactory serviceScopeFactory)
 | 
			
		||||
        {
 | 
			
		||||
            _relationService = relationService;
 | 
			
		||||
            _resourceService = resourceService;
 | 
			
		||||
            _eventPublisher = eventPublisher;
 | 
			
		||||
            _serviceScope = serviceScopeFactory.CreateScope();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        [OperDesc("添加角色")]
 | 
			
		||||
        public async Task AddAsync(RoleAddInput input)
 | 
			
		||||
        {
 | 
			
		||||
            await CheckInput(input);//检查参数
 | 
			
		||||
            var sysRole = input.Adapt<SysRole>();//实体转换
 | 
			
		||||
            sysRole.Code = YitIdHelper.NextId().ToString();//赋值Code
 | 
			
		||||
            if (await InsertAsync(sysRole))//插入数据
 | 
			
		||||
                RefreshCache();//刷新缓存
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        [OperDesc("删除角色")]
 | 
			
		||||
        public async Task DeleteAsync(params long[] input)
 | 
			
		||||
        {
 | 
			
		||||
            //获取所有ID
 | 
			
		||||
            var ids = input.ToList();
 | 
			
		||||
            if (ids.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                var sysRoles = await GetListAsync();//获取所有角色
 | 
			
		||||
                var hasSuperAdmin = sysRoles.Any(it => it.Code == RoleConst.SuperAdmin && ids.Contains(it.Id));//判断是否有超级管理员
 | 
			
		||||
                if (hasSuperAdmin) throw Oops.Bah($"不可删除系统内置超管角色");
 | 
			
		||||
 | 
			
		||||
                //数据库是string所以这里转下
 | 
			
		||||
                var targetIds = ids.Select(it => it.ToString()).ToList();
 | 
			
		||||
                //定义删除的关系
 | 
			
		||||
                var delRelations = new List<string> { CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE, CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION };
 | 
			
		||||
                //事务
 | 
			
		||||
                var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await DeleteByIdsAsync(ids.Cast<object>().ToArray());//删除按钮
 | 
			
		||||
 | 
			
		||||
                    //删除关系表角色与资源关系,角色与权限关系
 | 
			
		||||
                    await Context.Deleteable<SysRelation>().Where(it => ids.Contains(it.ObjectId) && delRelations.Contains(it.Category)).ExecuteCommandAsync();
 | 
			
		||||
                    //删除关系表角色与用户关系
 | 
			
		||||
                    await Context.Deleteable<SysRelation>().Where(it => targetIds.Contains(it.TargetId) && it.Category == CateGoryConst.Relation_SYS_USER_HAS_ROLE).ExecuteCommandAsync();
 | 
			
		||||
 | 
			
		||||
                });
 | 
			
		||||
                if (result.IsSuccess)//如果成功了
 | 
			
		||||
                {
 | 
			
		||||
                    RefreshCache();//刷新缓存
 | 
			
		||||
                    _relationService.RefreshCache(CateGoryConst.Relation_SYS_USER_HAS_ROLE);//关系表刷新SYS_USER_HAS_ROLE缓存
 | 
			
		||||
                    _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//关系表刷新SYS_ROLE_HAS_RESOURCE缓存
 | 
			
		||||
                    _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//关系表刷新SYS_ROLE_HAS_PERMISSION缓存
 | 
			
		||||
                    await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, ids);//清除角色下用户缓存
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //写日志
 | 
			
		||||
                    throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        [OperDesc("编辑角色")]
 | 
			
		||||
        public async Task EditAsync(RoleEditInput input)
 | 
			
		||||
        {
 | 
			
		||||
            //判断是否超管
 | 
			
		||||
            if (input.Code == RoleConst.SuperAdmin)
 | 
			
		||||
                throw Oops.Bah($"不可编辑超管角色");
 | 
			
		||||
            await CheckInput(input);//检查参数
 | 
			
		||||
            var role = await GetFirstAsync(it => it.Id == input.Id);//获取角色
 | 
			
		||||
            if (role != null)
 | 
			
		||||
            {
 | 
			
		||||
                var permissions = new List<SysRelation>();
 | 
			
		||||
 | 
			
		||||
                var sysRole = input.Adapt<SysRole>();//实体转换
 | 
			
		||||
                                                     //事务
 | 
			
		||||
                var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await UpdateAsync(sysRole);//更新角色
 | 
			
		||||
                    if (permissions.Any())//如果有授权权限就更新
 | 
			
		||||
                        await Context.Updateable(permissions).ExecuteCommandAsync();
 | 
			
		||||
                });
 | 
			
		||||
                if (result.IsSuccess)//如果成功了
 | 
			
		||||
                {
 | 
			
		||||
                    RefreshCache();//刷新缓存
 | 
			
		||||
                    if (permissions.Any())//如果有授权
 | 
			
		||||
                        _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//关系表刷新SYS_ROLE_HAS_PERMISSION缓存
 | 
			
		||||
                    await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id });//清除角色下用户缓存
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //写日志
 | 
			
		||||
                    throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取所有角色
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public override async Task<List<SysRole>> GetListAsync()
 | 
			
		||||
        {
 | 
			
		||||
            //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
            var sysRoles = _serviceScope.ServiceProvider.GetService<MemoryCache>().Get<List<SysRole>>(CacheConst.CACHE_SYSROLE, true);
 | 
			
		||||
            if (sysRoles == null)
 | 
			
		||||
            {
 | 
			
		||||
                //cache没有就去数据库拿
 | 
			
		||||
                sysRoles = await base.GetListAsync();
 | 
			
		||||
                if (sysRoles.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    //插入Cache
 | 
			
		||||
                    _serviceScope.ServiceProvider.GetService<MemoryCache>().Set(CacheConst.CACHE_SYSROLE, sysRoles, true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return sysRoles;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public async Task<List<long>> GetRoleIdListByUserIdAsync(long userId)
 | 
			
		||||
        {
 | 
			
		||||
            List<SysRole> cods = new();//角色代码集合
 | 
			
		||||
            var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);//根据用户ID获取角色ID
 | 
			
		||||
            var roleIdList = roleList.Select(x => x.TargetId.ToLong()).ToList();//角色ID列表
 | 
			
		||||
            return roleIdList;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public async Task<List<SysRole>> GetRoleListByUserIdAsync(long userId)
 | 
			
		||||
        {
 | 
			
		||||
            List<SysRole> cods = new();//角色代码集合
 | 
			
		||||
            var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);//根据用户ID获取角色ID
 | 
			
		||||
            var roleIdList = roleList.Select(x => x.TargetId.ToLong()).ToList();//角色ID列表
 | 
			
		||||
            if (roleIdList.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                cods = await GetListAsync(it => roleIdList.Contains(it.Id));
 | 
			
		||||
            }
 | 
			
		||||
            return cods;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        [OperDesc("角色授权")]
 | 
			
		||||
        public async Task GrantResourceAsync(GrantResourceInput input)
 | 
			
		||||
        {
 | 
			
		||||
            var menuIds = input.GrantInfoList.Select(it => it.MenuId).ToList();//菜单ID
 | 
			
		||||
            var extJsons = input.GrantInfoList.Select(it => it.ToJsonString()).ToList();//拓展信息
 | 
			
		||||
            var relationRoles = new List<SysRelation>();//要添加的角色资源和授权关系表
 | 
			
		||||
            var sysRole = (await GetListAsync()).Where(it => it.Id == input.Id).FirstOrDefault();//获取角色
 | 
			
		||||
            if (sysRole != null)
 | 
			
		||||
            {
 | 
			
		||||
                #region 角色资源处理
 | 
			
		||||
 | 
			
		||||
                //遍历角色列表
 | 
			
		||||
                for (int i = 0; i < menuIds.Count; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    //将角色资源添加到列表
 | 
			
		||||
                    relationRoles.Add(new SysRelation
 | 
			
		||||
                    {
 | 
			
		||||
                        ObjectId = sysRole.Id,
 | 
			
		||||
                        TargetId = menuIds[i].ToString(),
 | 
			
		||||
                        Category = CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE,
 | 
			
		||||
                        ExtJson = extJsons?[i]
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                #endregion 角色资源处理
 | 
			
		||||
 | 
			
		||||
                #region 角色权限处理
 | 
			
		||||
 | 
			
		||||
                var relationRolePer = new List<SysRelation>();//要添加的角色有哪些权限列表
 | 
			
		||||
 | 
			
		||||
                //获取菜单信息
 | 
			
		||||
                var menus = await GetMenuByMenuIds(menuIds);
 | 
			
		||||
                if (menus.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    //获取权限授权树
 | 
			
		||||
                    var permissions = PermissionUtil.PermissionTreeSelector(menus.Select(it => it.Component).ToList());
 | 
			
		||||
                    permissions.ForEach(it =>
 | 
			
		||||
                    {
 | 
			
		||||
                        //新建角色权限关系
 | 
			
		||||
                        relationRolePer.Add(new SysRelation
 | 
			
		||||
                        {
 | 
			
		||||
                            ObjectId = sysRole.Id,
 | 
			
		||||
                            TargetId = it.ApiRoute,
 | 
			
		||||
                            Category = CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION,
 | 
			
		||||
                            ExtJson = new RelationRolePermission
 | 
			
		||||
                            {
 | 
			
		||||
                                ApiUrl = it.ApiRoute,
 | 
			
		||||
                            }.ToJsonString()
 | 
			
		||||
                        });
 | 
			
		||||
                    });
 | 
			
		||||
                }
 | 
			
		||||
                relationRoles.AddRange(relationRolePer);//合并列表
 | 
			
		||||
 | 
			
		||||
                #endregion 角色权限处理
 | 
			
		||||
 | 
			
		||||
                #region 保存数据库
 | 
			
		||||
 | 
			
		||||
                //事务
 | 
			
		||||
                var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    //删除老的
 | 
			
		||||
                    await Context.Deleteable<SysRelation>().Where(it => it.ObjectId == sysRole.Id
 | 
			
		||||
                    &&
 | 
			
		||||
                    (it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION
 | 
			
		||||
                    || it.Category == CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE)
 | 
			
		||||
                    )
 | 
			
		||||
                    .ExecuteCommandAsync();
 | 
			
		||||
                    await Context.Insertable(relationRoles).ExecuteCommandAsync();
 | 
			
		||||
                });
 | 
			
		||||
                if (result.IsSuccess)//如果成功了
 | 
			
		||||
                {
 | 
			
		||||
                    _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//刷新关系缓存
 | 
			
		||||
                    _relationService.RefreshCache(CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);//刷新关系缓存
 | 
			
		||||
                    await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id });//发送事件清除角色下用户缓存
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    //写日志
 | 
			
		||||
                    throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                #endregion 保存数据库
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        [OperDesc("用户授权")]
 | 
			
		||||
        public async Task GrantUserAsync(GrantUserInput input)
 | 
			
		||||
        {
 | 
			
		||||
            var sysRelations = new List<SysRelation>();//关系列表
 | 
			
		||||
 | 
			
		||||
            //遍历用户ID
 | 
			
		||||
            input.GrantInfoList.ForEach(it =>
 | 
			
		||||
            {
 | 
			
		||||
                sysRelations.Add(new SysRelation
 | 
			
		||||
                {
 | 
			
		||||
                    ObjectId = it,
 | 
			
		||||
                    TargetId = input.Id.ToString(),
 | 
			
		||||
                    Category = CateGoryConst.Relation_SYS_USER_HAS_ROLE
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            //事务
 | 
			
		||||
            var result = await itenant.UseTranAsync(async () =>
 | 
			
		||||
            {
 | 
			
		||||
                //删除老的
 | 
			
		||||
                await Context.Deleteable<SysRelation>().Where(it => it.TargetId == input.Id.ToString() && it.Category == CateGoryConst.Relation_SYS_USER_HAS_ROLE).ExecuteCommandAsync();
 | 
			
		||||
                await Context.Insertable(sysRelations).ExecuteCommandAsync();//添加新的
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
            if (result.IsSuccess)//如果成功了
 | 
			
		||||
            {
 | 
			
		||||
                _relationService.RefreshCache(CateGoryConst.Relation_SYS_USER_HAS_ROLE);//刷新关系表SYS_USER_HAS_ROLE缓存
 | 
			
		||||
                await _eventPublisher.PublishAsync(EventSubscriberConst.ClearUserCache, new List<long> { input.Id.Value });//清除角色下用户缓存
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                //写日志
 | 
			
		||||
                throw Oops.Oh(result.ErrorMessage);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public async Task<RoleOwnResourceOutput> OwnResourceAsync(long input)
 | 
			
		||||
        {
 | 
			
		||||
            RoleOwnResourceOutput roleOwnResource = new() { Id = input };//定义结果集
 | 
			
		||||
            List<RelationRoleResuorce> GrantInfoList = new();//已授权信息集合
 | 
			
		||||
                                                             //获取关系列表
 | 
			
		||||
            var relations = await _relationService.GetRelationListByObjectIdAndCategoryAsync(input, CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);
 | 
			
		||||
            //遍历关系表
 | 
			
		||||
            relations.ForEach(it =>
 | 
			
		||||
            {
 | 
			
		||||
                //将扩展信息转为实体
 | 
			
		||||
                var relationRole = it.ExtJson.FromJsonString<RelationRoleResuorce>();
 | 
			
		||||
                GrantInfoList.Add(relationRole);//添加到已授权信息
 | 
			
		||||
            });
 | 
			
		||||
            roleOwnResource.GrantInfoList = GrantInfoList;//赋值已授权信息
 | 
			
		||||
            return roleOwnResource;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public async Task<List<long>> OwnUserAsync(long input)
 | 
			
		||||
        {
 | 
			
		||||
            //获取关系列表
 | 
			
		||||
            var relations = await _relationService.GetRelationListByTargetIdAndCategoryAsync(input.ToString(), CateGoryConst.Relation_SYS_USER_HAS_ROLE);
 | 
			
		||||
            return relations.Select(it => it.ObjectId).ToList();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        public async Task<ISqlSugarPagedList<SysRole>> PageAsync(RolePageInput input)
 | 
			
		||||
        {
 | 
			
		||||
            var query = Context.Queryable<SysRole>()
 | 
			
		||||
                             .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
            for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
            {
 | 
			
		||||
                query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
            }
 | 
			
		||||
            query = query.OrderBy(it => it.SortCode);//排序
 | 
			
		||||
 | 
			
		||||
            var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
            return pageInfo;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public void RefreshCache()
 | 
			
		||||
        {
 | 
			
		||||
            _serviceScope.ServiceProvider.GetService<MemoryCache>().Remove(CacheConst.CACHE_SYSROLE);//删除KEY
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public async Task RefreshResourceAsync(long? menuId = null)
 | 
			
		||||
        {
 | 
			
		||||
            var data = await GetListAsync();
 | 
			
		||||
            foreach (var item in data)
 | 
			
		||||
            {
 | 
			
		||||
                var r1 = await OwnResourceAsync(item.Id);
 | 
			
		||||
                if (menuId == null || r1.GrantInfoList.Any(a => a.MenuId == menuId))
 | 
			
		||||
                {
 | 
			
		||||
                    await GrantResourceAsync(new GrantResourceInput() { Id = item.Id, GrantInfoList = r1.GrantInfoList });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        public async Task<List<SysRole>> RoleSelectorAsync(string searchKey = null)
 | 
			
		||||
        {
 | 
			
		||||
            var result = await Context.Queryable<SysRole>()
 | 
			
		||||
                             .WhereIF(!string.IsNullOrEmpty(searchKey), it => it.Name.Contains(searchKey))//根据关键字查询
 | 
			
		||||
                             .ToListAsync();
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #region 方法
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 检查输入参数
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="sysRole"></param>
 | 
			
		||||
        private async Task CheckInput(SysRole sysRole)
 | 
			
		||||
        {
 | 
			
		||||
            var sysRoles = await GetListAsync();//获取所有
 | 
			
		||||
            var repeatName = sysRoles.Any(it => it.Name == sysRole.Name && it.Id != sysRole.Id);//是否有重复角色名称
 | 
			
		||||
            if (repeatName)//如果有
 | 
			
		||||
            {
 | 
			
		||||
                throw Oops.Bah($"存在重复的角色:{sysRole.Name}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 根据菜单ID获取菜单
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="menuIds"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        private async Task<List<SysResource>> GetMenuByMenuIds(List<long> menuIds)
 | 
			
		||||
        {
 | 
			
		||||
            //获取所有菜单
 | 
			
		||||
            var menuList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.MENU);
 | 
			
		||||
            //获取菜单信息
 | 
			
		||||
            var menus = menuList.Where(it => menuIds.Contains(it.Id)).ToList();
 | 
			
		||||
 | 
			
		||||
            return menus;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #endregion 方法
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,56 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话分页查询
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SessionPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("账号")]
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录IP
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("最新登录IP")]
 | 
			
		||||
    public string LatestLoginIp { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 姓名
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("姓名")]
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 退出参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class ExitVerificatInput : BaseIdInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 验证ID列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "VerificatIds不能为空")]
 | 
			
		||||
    public List<long> VerificatIds { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,62 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话输出
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SessionOutput : PrimaryKeyEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("账号")]
 | 
			
		||||
    [DataTable(Order = 1, IsShow = true, Sortable = true)]
 | 
			
		||||
    public virtual string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录ip
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("最新登录ip")]
 | 
			
		||||
    [DataTable(Order = 3, IsShow = true, Sortable = true)]
 | 
			
		||||
    public string LatestLoginIp { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 最新登录时间
 | 
			
		||||
    ///</summary>
 | 
			
		||||
    [Description("最新登录时间")]
 | 
			
		||||
    [DataTable(Order = 4, IsShow = true, Sortable = true)]
 | 
			
		||||
    public DateTime? LatestLoginTime { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 在线状态
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("在线状态")]
 | 
			
		||||
    [DataTable(Order = 2, IsShow = true, Sortable = true)]
 | 
			
		||||
    public bool OnlineStatus { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌数量
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌数量")]
 | 
			
		||||
    [DataTable(Order = 5, IsShow = true, Sortable = false)]
 | 
			
		||||
    public int VerificatCount { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌信息集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌列表")]
 | 
			
		||||
    public List<VerificatInfo> VerificatSignList { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,40 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 会话管理服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISessionService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 强退会话
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户ID</param>
 | 
			
		||||
    Task ExitSessionAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 强退verificat
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">verificat列表</param>
 | 
			
		||||
    Task ExitVerificatAsync(ExitVerificatInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 会话分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>会话列表</returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SessionOutput>> PageAsync(SessionPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,116 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="ISessionService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class SessionService : DbRepository<SysUser>, ISessionService
 | 
			
		||||
{
 | 
			
		||||
    private readonly INoticeService _noticeService;
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="ISessionService"/>
 | 
			
		||||
    public SessionService(IVerificatService verificatService, INoticeService noticeService)
 | 
			
		||||
    {
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
        _noticeService = noticeService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("强退会话")]
 | 
			
		||||
    public async Task ExitSessionAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        //verificat列表
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input);
 | 
			
		||||
        //从列表中删除
 | 
			
		||||
        await _verificatService.SetVerificatIdAsync(input, new());
 | 
			
		||||
        var message = "您已被强制下线!";
 | 
			
		||||
        await _noticeService.LogoutAsync(input, verificatInfos, message);//通知下线
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("强退令牌")]
 | 
			
		||||
    public async Task ExitVerificatAsync(ExitVerificatInput input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取该用户的verificat信息
 | 
			
		||||
        List<VerificatInfo> verificatInfos = await _verificatService.GetVerificatIdAsync(input.Id);
 | 
			
		||||
 | 
			
		||||
        //踢掉包含verificat列表的verificat信息
 | 
			
		||||
        var setVerificats = verificatInfos.Where(it => !input.VerificatIds.Contains(it.Id)).ToList();
 | 
			
		||||
        var deleteVerificats = verificatInfos.Where(it => input.VerificatIds.Contains(it.Id)).ToList();
 | 
			
		||||
        await _verificatService.SetVerificatIdAsync(input.Id, setVerificats);//如果还有verificat则更新verificat
 | 
			
		||||
 | 
			
		||||
        var message = "您已被强制下线!";
 | 
			
		||||
        await _noticeService.LogoutAsync(input.Id, deleteVerificats, message);//通知下线
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<SessionOutput>> PageAsync(SessionPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysUser>()
 | 
			
		||||
              .WhereIF(!string.IsNullOrEmpty(input.Account), it => it.Account.Contains(input.Account))//根据账号查询
 | 
			
		||||
              .WhereIF(!string.IsNullOrEmpty(input.LatestLoginIp), it => it.LatestLoginIp.Contains(input.LatestLoginIp))//根据IP查询
 | 
			
		||||
              .OrderBy(it => it.LatestLoginTime, OrderByType.Desc)
 | 
			
		||||
              .Select<SessionOutput>()
 | 
			
		||||
              .Mapper(async it =>
 | 
			
		||||
              {
 | 
			
		||||
                  var verificatInfos = await _verificatService.GetVerificatIdAsync(it.Id);
 | 
			
		||||
                  if (verificatInfos != null)
 | 
			
		||||
                  {
 | 
			
		||||
                      SessionService.GetVerificatInfos(ref verificatInfos);//获取剩余时间
 | 
			
		||||
                      it.VerificatCount = verificatInfos.Count;//令牌数量
 | 
			
		||||
                      it.VerificatSignList = verificatInfos;//令牌列表
 | 
			
		||||
 | 
			
		||||
                      //如果有客户端ID就是在线
 | 
			
		||||
                      it.OnlineStatus = verificatInfos.Any(it => it.ClientIds.Count > 0);
 | 
			
		||||
                  }
 | 
			
		||||
                  else
 | 
			
		||||
                  {
 | 
			
		||||
                      it.VerificatSignList = new();
 | 
			
		||||
                  }
 | 
			
		||||
 | 
			
		||||
              });
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取verificat剩余时间信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
 | 
			
		||||
    {
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            var now = DateTimeExtensions.CurrentDateTime;
 | 
			
		||||
            it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
 | 
			
		||||
            var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
 | 
			
		||||
            var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,67 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 单页输入参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SpaAddInput : SysResource
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 路径
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Component不能为空")]
 | 
			
		||||
    public override string Component { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 图标
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Icon不能为空")]
 | 
			
		||||
    public override string Icon { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 菜单类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public override TargetTypeEnum TargetType { get; set; } = TargetTypeEnum.SELF;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 标题
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Title不能为空")]
 | 
			
		||||
    public override string Title { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 单页输入参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SpaPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 跳转类型
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public TargetTypeEnum TargetType { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 单页修改参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SpaEditInput : SpaAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,49 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 单页服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISpaService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加单页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(SpaAddInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 删除单页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">删除参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task DeleteAsync(params long[] input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑单页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">编辑参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditAsync(SpaEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<ISqlSugarPagedList<SysResource>> PageAsync(SpaPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,125 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Mapster;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="ISpaService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class SpaService : DbRepository<SysResource>, ISpaService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IResourceService _resourceService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="ISpaService"/>
 | 
			
		||||
    public SpaService(IResourceService resourceService)
 | 
			
		||||
    {
 | 
			
		||||
        this._resourceService = resourceService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("添加单页")]
 | 
			
		||||
    public async Task AddAsync(SpaAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        CheckInput(input);//检查参数
 | 
			
		||||
        input.Code = YitIdHelper.NextId().ToString();//code取随机值
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
        if (await InsertAsync(sysResource))//插入数据
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("删除单页")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] input)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有ID
 | 
			
		||||
        var ids = input.ToList();
 | 
			
		||||
        if (ids.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            //获取所有
 | 
			
		||||
            var resourceList = await _resourceService.GetListByCategoryAsync(ResourceCategoryEnum.SPA);
 | 
			
		||||
            //找到要删除的
 | 
			
		||||
            var sysresources = resourceList.Where(it => ids.Contains(it.Id)).ToList();
 | 
			
		||||
            //查找内置单页面
 | 
			
		||||
            var system = sysresources.Where(it => it.Code == ResourceConst.System).FirstOrDefault();
 | 
			
		||||
            if (system != null)
 | 
			
		||||
                throw Oops.Bah($"不可删除系统内置单页面:{system.Title}");
 | 
			
		||||
            //删除菜单
 | 
			
		||||
            await DeleteAsync(sysresources);
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("编辑单页")]
 | 
			
		||||
    public async Task EditAsync(SpaEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        CheckInput(input);//检查参数
 | 
			
		||||
        var sysResource = input.Adapt<SysResource>();//实体转换
 | 
			
		||||
        if (await UpdateAsync(sysResource))//更新数据
 | 
			
		||||
            _resourceService.RefreshCache(ResourceCategoryEnum.SPA);//刷新缓存
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<ISqlSugarPagedList<SysResource>> PageAsync(SpaPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysResource>()
 | 
			
		||||
                         .Where(it => it.Category == ResourceCategoryEnum.SPA)//单页
 | 
			
		||||
                         .WhereIF(input.TargetType != 0, it => it.TargetType == input.TargetType)//根据菜单类型查询
 | 
			
		||||
                         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Title.Contains(input.SearchKey) || it.Component.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
        for (int i = input.SortField.Count - 1; i >= 0; i--)
 | 
			
		||||
        {
 | 
			
		||||
            query = query.OrderByIF(!string.IsNullOrEmpty(input.SortField[i]), $"{input.SortField[i]} {(input.SortDesc[i] ? "desc" : "asc")}");
 | 
			
		||||
        }
 | 
			
		||||
        query = query.OrderBy(it => it.SortCode);//排序
 | 
			
		||||
 | 
			
		||||
        var pageInfo = await query.ToPagedListAsync(input.Current, input.Size);//分页
 | 
			
		||||
        return pageInfo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysResource"></param>
 | 
			
		||||
    private void CheckInput(SysResource sysResource)
 | 
			
		||||
    {
 | 
			
		||||
        //判断菜单类型
 | 
			
		||||
        if (sysResource.TargetType == TargetTypeEnum.SELF)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(sysResource.Component))
 | 
			
		||||
            {
 | 
			
		||||
                throw Oops.Bah($"组件地址不能为空");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (sysResource.TargetType == TargetTypeEnum.BLANK)//如果是内链或者外链
 | 
			
		||||
        {
 | 
			
		||||
            sysResource.Component = null;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Bah($"单页类型错误:{sysResource.TargetType}");//都不是
 | 
			
		||||
        }
 | 
			
		||||
        //设置为单页
 | 
			
		||||
        sysResource.Category = ResourceCategoryEnum.SPA;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,76 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
using SqlSugar;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 添加用户参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserAddInput : SysUser
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public override string Account { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 编辑用户参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserEditInput : UserAddInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "账号不能为空"), MinLength(3, ErrorMessage = "账号不能少于4个字符")]
 | 
			
		||||
    public override string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户分页查询参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserPageInput : BasePageInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 动态查询条件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Expressionable<SysUser> Expression { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户授权角色参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserGrantRoleInput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public long Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 授权权限信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Required(ErrorMessage = "RoleIdList不能为空")]
 | 
			
		||||
    public List<long> RoleIdList { get; set; }
 | 
			
		||||
}
 | 
			
		||||