Compare commits
	
		
			2123 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					7314c8901d | ||
| 
						 | 
					4e9f02b48c | ||
| 
						 | 
					9ae7602cb4 | ||
| 
						 | 
					aa8aa36aef | ||
| 
						 | 
					0174f7c6f2 | ||
| 
						 | 
					df9e7d6ff1 | ||
| 
						 | 
					b40ca920d3 | ||
| 
						 | 
					5a4b0a0e93 | ||
| 
						 | 
					a879edd68b | ||
| 
						 | 
					62e0a6ee9d | ||
| 
						 | 
					765e5564d4 | ||
| 
						 | 
					10eecac19b | ||
| 
						 | 
					59241b8faa | ||
| 
						 | 
					52b3097f04 | ||
| 
						 | 
					d922296b70 | ||
| 
						 | 
					aec91da28b | ||
| 
						 | 
					013ff394be | ||
| 
						 | 
					081e07473d | ||
| 
						 | 
					d33d900592 | ||
| 
						 | 
					29365c4ef9 | ||
| 
						 | 
					17a6189089 | ||
| 
						 | 
					003b8a3763 | ||
| 
						 | 
					1c7f8b5cab | ||
| 
						 | 
					b7ff9ffca2 | ||
| 
						 | 
					9bba9bda76 | ||
| 
						 | 
					ec2fcc75d3 | ||
| 
						 | 
					57a4038577 | ||
| 
						 | 
					b78a76e60f | ||
| 
						 | 
					28bd751d44 | ||
| 
						 | 
					dcba7b9810 | ||
| 
						 | 
					7f1a741ce6 | ||
| 
						 | 
					ca2b17d433 | ||
| 
						 | 
					af589eac10 | ||
| 
						 | 
					573670f1f5 | ||
| 
						 | 
					f3ec85a03d | ||
| 
						 | 
					c94308454f | ||
| 
						 | 
					c9c2b2b69d | ||
| 
						 | 
					40574b776f | ||
| 
						 | 
					f426c1533d | ||
| 
						 | 
					4a3b5163f1 | ||
| 
						 | 
					3a0946d357 | ||
| 
						 | 
					9dd21c644c | ||
| 
						 | 
					6713190a23 | ||
| 
						 | 
					9f24533425 | ||
| 
						 | 
					ed1d9374eb | ||
| 
						 | 
					f14d27129e | ||
| 
						 | 
					5b04f02fbe | ||
| 
						 | 
					227080e332 | ||
| 
						 | 
					1e87482a49 | ||
| 
						 | 
					054d31c3ea | ||
| 
						 | 
					3a068a7b03 | ||
| 
						 | 
					b12e923c99 | ||
| 
						 | 
					ab33eed8d3 | ||
| 
						 | 
					d930a9a8eb | ||
| 
						 | 
					af381fce12 | ||
| 
						 | 
					b64ac0539e | ||
| 
						 | 
					541c60b363 | ||
| 
						 | 
					824e95f7cb | ||
| 
						 | 
					38f7850196 | ||
| 
						 | 
					bef9de88e2 | ||
| 
						 | 
					48cd5e7c7f | ||
| 
						 | 
					3b44fda51c | ||
| 
						 | 
					dbfc9a5bb4 | ||
| 
						 | 
					1b758aa41a | ||
| 
						 | 
					43bdc70899 | ||
| 
						 | 
					fadda000a6 | ||
| 
						 | 
					45a8c91a5a | ||
| 
						 | 
					8e938f18be | ||
| 
						 | 
					ab1b364c54 | ||
| 
						 | 
					5ec65b2fb0 | ||
| 
						 | 
					926eced724 | ||
| 
						 | 
					f7f8802272 | ||
| 
						 | 
					c6910dff02 | ||
| 
						 | 
					ad299d0dbb | ||
| 
						 | 
					8b124d1050 | ||
| 
						 | 
					ff41080dbd | ||
| 
						 | 
					0e28606e3d | ||
| 
						 | 
					6a025ceee5 | ||
| 
						 | 
					6b2e53d6dc | ||
| 
						 | 
					b989aa5561 | ||
| 
						 | 
					f5b0b7ebd2 | ||
| 
						 | 
					16881ae076 | ||
| 
						 | 
					af04112656 | ||
| 
						 | 
					a2863112dc | ||
| 
						 | 
					f531e4dfc5 | ||
| 
						 | 
					8db9b32ba7 | ||
| 
						 | 
					dd5691cbef | ||
| 
						 | 
					de48b32af3 | ||
| 
						 | 
					600b5042a1 | ||
| 
						 | 
					aac77029da | ||
| 
						 | 
					e50205f557 | ||
| 
						 | 
					e227411d1f | ||
| 
						 | 
					2de0ed793f | ||
| 
						 | 
					cb0276f273 | ||
| 
						 | 
					562b3f17c9 | ||
| 
						 | 
					0f78f81c1c | ||
| 
						 | 
					6594937d0a | ||
| 
						 | 
					64bc6084be | ||
| 
						 | 
					20434d5bd2 | ||
| 
						 | 
					9ecc9380e6 | ||
| 
						 | 
					a57c55080b | ||
| 
						 | 
					b8ca06c6be | ||
| 
						 | 
					5aff6461a1 | ||
| 
						 | 
					6cf53fefec | ||
| 
						 | 
					45132f3503 | ||
| 
						 | 
					f1e78a0e8a | ||
| 
						 | 
					0bf28ec275 | ||
| 
						 | 
					41f8412c97 | ||
| 
						 | 
					c535974362 | ||
| 
						 | 
					1860c5f215 | ||
| 
						 | 
					6d778b2d39 | ||
| 
						 | 
					f48b99c259 | ||
| 
						 | 
					3c73b93051 | ||
| 
						 | 
					98f3f2d519 | ||
| 
						 | 
					b76b4e8d68 | ||
| 
						 | 
					07285a7c61 | ||
| 
						 | 
					03c0dfef37 | ||
| 
						 | 
					6ef6929c35 | ||
| 
						 | 
					e3c0c173f0 | ||
| 
						 | 
					7d64e058d4 | ||
| 
						 | 
					e97ee9b64b | ||
| 
						 | 
					6a03e39eeb | ||
| 
						 | 
					525ec740b5 | ||
| 
						 | 
					b790cf5f4e | ||
| 
						 | 
					d1248811fd | ||
| 
						 | 
					022d016e8e | ||
| 
						 | 
					f73245e650 | ||
| 
						 | 
					484461fa05 | ||
| 
						 | 
					7e0b7aff2a | ||
| 
						 | 
					6c450dcb09 | ||
| 
						 | 
					227f44283f | ||
| 
						 | 
					74f6e79625 | ||
| 
						 | 
					cec43e2ce8 | ||
| 
						 | 
					7553b258bb | ||
| 
						 | 
					8bdbdc117e | ||
| 
						 | 
					0e206be296 | ||
| 
						 | 
					00b7353433 | ||
| 
						 | 
					44e7a83593 | ||
| 
						 | 
					dd68d555d4 | ||
| 
						 | 
					0456296103 | ||
| 
						 | 
					a1b66277ff | ||
| 
						 | 
					50758b79bc | ||
| 
						 | 
					06a1f902ad | ||
| 
						 | 
					58f8b23b7c | ||
| 
						 | 
					9e7c348b15 | ||
| 
						 | 
					5f5ff8b43b | ||
| 
						 | 
					f626b4e5fc | ||
| 
						 | 
					bc23200e66 | ||
| 
						 | 
					95ab59fd5a | ||
| 
						 | 
					0bbee003b0 | ||
| 
						 | 
					d20cb7a928 | ||
| 
						 | 
					952c134aba | ||
| 
						 | 
					5533d980e3 | ||
| 
						 | 
					aa0213818f | ||
| 
						 | 
					af88ffd57c | ||
| 
						 | 
					87447b1c2a | ||
| 
						 | 
					384a12880b | ||
| 
						 | 
					791f91f377 | ||
| 
						 | 
					4f8b405cc3 | ||
| 
						 | 
					e1b7af201a | ||
| 
						 | 
					a04b7817a9 | ||
| 
						 | 
					21bf83aec7 | ||
| 
						 | 
					d060d5179d | ||
| 
						 | 
					e73dd58e46 | ||
| 
						 | 
					a34e4a6942 | ||
| 
						 | 
					a7ee6d78b5 | ||
| 
						 | 
					0a6651ac11 | ||
| 
						 | 
					fa25053c13 | ||
| 
						 | 
					61dd4edeb5 | ||
| 
						 | 
					dbaeff302b | ||
| 
						 | 
					6cbe7ebcf2 | ||
| 
						 | 
					2826e08c8c | ||
| 
						 | 
					e30e337efa | ||
| 
						 | 
					39788e0357 | ||
| 
						 | 
					c47363df71 | ||
| 
						 | 
					4199453038 | ||
| 
						 | 
					db991081c8 | ||
| 
						 | 
					24f946a727 | ||
| 
						 | 
					772dae047a | ||
| 
						 | 
					020f440fdd | ||
| 
						 | 
					c88fc5ec09 | ||
| 
						 | 
					475849fbef | ||
| 
						 | 
					1d63228fb2 | ||
| 
						 | 
					c918c0c010 | ||
| 
						 | 
					1028179983 | ||
| 
						 | 
					686f61ff90 | ||
| 
						 | 
					ae43fadca3 | ||
| 
						 | 
					1bf05780b3 | ||
| 
						 | 
					3e30daa1d3 | ||
| 
						 | 
					54ef1233b1 | ||
| 
						 | 
					4413633de7 | ||
| 
						 | 
					c76e20d146 | ||
| 
						 | 
					36d53b7ce4 | ||
| 
						 | 
					0b0bce2761 | ||
| 
						 | 
					d47a9be03c | ||
| 
						 | 
					bc32d7ae7a | ||
| 
						 | 
					e6747a2c2c | ||
| 
						 | 
					3fb48cb64e | ||
| 
						 | 
					381db1cf53 | ||
| 
						 | 
					d3939aeb9d | ||
| 
						 | 
					5956f90557 | ||
| 
						 | 
					7f54ecfe5d | ||
| 
						 | 
					e7bda99c8b | ||
| 
						 | 
					ea7ab5a2bf | ||
| 
						 | 
					ee566d199a | ||
| 
						 | 
					56eca454b1 | ||
| 
						 | 
					d057974c3e | ||
| 
						 | 
					9514e7fdea | ||
| 
						 | 
					e546113873 | ||
| 
						 | 
					60f1139355 | ||
| 
						 | 
					1b18b59054 | ||
| 
						 | 
					afec0187e9 | ||
| 
						 | 
					d0d7e1ccf8 | ||
| 
						 | 
					d72376b4b7 | ||
| 
						 | 
					3be60cb305 | ||
| 
						 | 
					75b1f351a1 | ||
| 
						 | 
					07185f8018 | ||
| 
						 | 
					2f081b62a7 | ||
| 
						 | 
					bbce2a11e1 | ||
| 
						 | 
					96ab609157 | ||
| 
						 | 
					d961d56a35 | ||
| 
						 | 
					82a5335a92 | ||
| 
						 | 
					729a23949c | ||
| 
						 | 
					a1b0419d7a | ||
| 
						 | 
					7a23015b9d | ||
| 
						 | 
					9cbe0dbae1 | ||
| 
						 | 
					4f139b7d03 | ||
| 
						 | 
					1e925e2560 | ||
| 
						 | 
					a42e1e3af3 | ||
| 
						 | 
					799caec7ee | ||
| 
						 | 
					dd7d30498f | ||
| 
						 | 
					e9b1720e98 | ||
| 
						 | 
					5f41371bdb | ||
| 
						 | 
					5226bfae20 | ||
| 
						 | 
					d1eab168b7 | ||
| 
						 | 
					8760518c10 | ||
| 
						 | 
					6719a352c2 | ||
| 
						 | 
					8449b6c6b8 | ||
| 
						 | 
					0550eaf465 | ||
| 
						 | 
					bdc9eb44ec | ||
| 
						 | 
					37b99cb1b0 | ||
| 
						 | 
					492381e4fc | ||
| 
						 | 
					27d65dd799 | ||
| 
						 | 
					434724310c | ||
| 
						 | 
					e5ad5a8521 | ||
| 
						 | 
					3942819fed | ||
| 
						 | 
					1cc5f538aa | ||
| 
						 | 
					b0d8534ea4 | ||
| 
						 | 
					5b2bc38670 | ||
| 
						 | 
					b36aa3557c | ||
| 
						 | 
					a1a525e6e3 | ||
| 
						 | 
					14453cc437 | ||
| 
						 | 
					db760ba5d8 | ||
| 
						 | 
					4ae692008f | ||
| 
						 | 
					f37449f9ac | ||
| 
						 | 
					8b7348b3d4 | ||
| 
						 | 
					709cbb7b2e | ||
| 
						 | 
					187f1a4ce2 | ||
| 
						 | 
					0f2ebd3a54 | ||
| 
						 | 
					86b63c2792 | ||
| 
						 | 
					cec2e71b41 | ||
| 
						 | 
					9380f3e1ec | ||
| 
						 | 
					76c2934e12 | ||
| 
						 | 
					9714c35d4b | ||
| 
						 | 
					0d9beb713a | ||
| 
						 | 
					c5f8d0e0b7 | ||
| 
						 | 
					5e8f4b075d | ||
| 
						 | 
					4b53fca3db | ||
| 
						 | 
					5d9c030a09 | ||
| 
						 | 
					bacbb9a28e | ||
| 
						 | 
					7c287e0c03 | ||
| 
						 | 
					10f675137d | ||
| 
						 | 
					6bdb5328e3 | ||
| 
						 | 
					14f529ac4e | ||
| 
						 | 
					ffe96d9ef1 | ||
| 
						 | 
					335f264caa | ||
| 
						 | 
					b73e86ea9f | ||
| 
						 | 
					2bc2c4c57b | ||
| 
						 | 
					6f87c2f03a | ||
| 
						 | 
					75e677af88 | ||
| 
						 | 
					9ffe355396 | ||
| 
						 | 
					60b0fb23f8 | ||
| 
						 | 
					1c05ffc6ed | ||
| 
						 | 
					8cd4e43c68 | ||
| 
						 | 
					8486d28310 | ||
| 
						 | 
					e8aadda185 | ||
| 
						 | 
					fcfd0428ab | ||
| 
						 | 
					ddcc087800 | ||
| 
						 | 
					d574de54b4 | ||
| 
						 | 
					1682ac03ac | ||
| 
						 | 
					447dbece1c | ||
| 
						 | 
					dcca901b17 | ||
| 
						 | 
					a32c779074 | ||
| 
						 | 
					187c0f9829 | ||
| 
						 | 
					6c970c88ab | ||
| 
						 | 
					28a4520c77 | ||
| 
						 | 
					02d0dcdff7 | ||
| 
						 | 
					427d4e31c9 | ||
| 
						 | 
					c60f7aac44 | ||
| 
						 | 
					42a7758a34 | ||
| 
						 | 
					e0bade4670 | ||
| 
						 | 
					6af83152de | ||
| 
						 | 
					091a30045d | ||
| 
						 | 
					b2c8ae0c33 | ||
| 
						 | 
					8b6e0982c6 | ||
| 
						 | 
					caa3ebd600 | ||
| 
						 | 
					d248e047b3 | ||
| 
						 | 
					25a95a1de0 | ||
| 
						 | 
					d506b2c692 | ||
| 
						 | 
					24c672c2b9 | ||
| 
						 | 
					c407e0e83e | ||
| 
						 | 
					b1346698d6 | ||
| 
						 | 
					5c01129567 | ||
| 
						 | 
					cd7ce53bd0 | ||
| 
						 | 
					4c95c20061 | ||
| 
						 | 
					5339db5058 | ||
| 
						 | 
					623e932f8c | ||
| 
						 | 
					43659de4f4 | ||
| 
						 | 
					2a661eda42 | ||
| 
						 | 
					c85a93a17b | ||
| 
						 | 
					8ab36cb3e1 | ||
| 
						 | 
					72820d0708 | ||
| 
						 | 
					95b551d4ba | ||
| 
						 | 
					68af9a0695 | ||
| 
						 | 
					6953c3989b | ||
| 
						 | 
					cd4cb385d5 | ||
| 
						 | 
					f4f6d595fc | ||
| 
						 | 
					d2356f00e9 | ||
| 
						 | 
					e408136196 | ||
| 
						 | 
					c355968add | ||
| 
						 | 
					783f878947 | ||
| 
						 | 
					4faf41e482 | ||
| 
						 | 
					d8964b6ed2 | ||
| 
						 | 
					b5b999941c | ||
| 
						 | 
					deeba8c4de | ||
| 
						 | 
					9e48729eb0 | ||
| 
						 | 
					936e374c05 | ||
| 
						 | 
					0f3524b114 | ||
| 
						 | 
					d62de3d72c | ||
| 
						 | 
					957a80da4b | ||
| 
						 | 
					1dccd70f7c | ||
| 
						 | 
					006a5d4e61 | ||
| 
						 | 
					ce7cdcf54e | ||
| 
						 | 
					c1ebb4a79b | ||
| 
						 | 
					41964ae1de | ||
| 
						 | 
					66cf34adf7 | ||
| 
						 | 
					9e9e0009cf | ||
| 
						 | 
					d5cd9660fc | ||
| 
						 | 
					12a7803653 | ||
| 
						 | 
					8f7bef6a20 | ||
| 
						 | 
					a0715b830c | ||
| 
						 | 
					f87ce10dc4 | ||
| 
						 | 
					ee4936b8c9 | ||
| 
						 | 
					3cb2592660 | ||
| 
						 | 
					574c901b40 | ||
| 
						 | 
					7590ba4ad5 | ||
| 
						 | 
					39ce18f8bb | ||
| 
						 | 
					777a4f9d3f | ||
| 
						 | 
					b91a938787 | ||
| 
						 | 
					c903abdb1b | ||
| 
						 | 
					3279bc0580 | ||
| 
						 | 
					bd32c9f49e | ||
| 
						 | 
					5507088e3d | ||
| 
						 | 
					37ad48ae4a | ||
| 
						 | 
					120c6e6d87 | ||
| 
						 | 
					866e907b7e | ||
| 
						 | 
					2bd8c2cb10 | ||
| 
						 | 
					c6c9919178 | ||
| 
						 | 
					575bf9d1e0 | ||
| 
						 | 
					82a56e0285 | ||
| 
						 | 
					25d5f7c132 | ||
| 
						 | 
					a3e9ecf30f | ||
| 
						 | 
					ec5ff0a07f | ||
| 
						 | 
					be836d30c5 | ||
| 
						 | 
					42b1529a5f | ||
| 
						 | 
					47708c4807 | ||
| 
						 | 
					d2a51e004c | ||
| 
						 | 
					ab14230101 | ||
| 
						 | 
					4bcf8c1f78 | ||
| 
						 | 
					44d00e9da3 | ||
| 
						 | 
					d7e6a4493c | ||
| 
						 | 
					53e89d8c54 | ||
| 
						 | 
					4497c13634 | ||
| 
						 | 
					4a35fade46 | ||
| 
						 | 
					6cba0601fd | ||
| 
						 | 
					ed4332ea78 | ||
| 
						 | 
					aba069cec5 | ||
| 
						 | 
					8a82ac0a11 | ||
| 
						 | 
					1cd1456d75 | ||
| 
						 | 
					b791a3eb10 | ||
| 
						 | 
					3b22a8b170 | ||
| 
						 | 
					419e8214ca | ||
| 
						 | 
					b9f8571f0f | ||
| 
						 | 
					c6d9a9d7f8 | ||
| 
						 | 
					310aba6ccb | ||
| 
						 | 
					9bd89ac4f6 | ||
| 
						 | 
					ea6a51dca9 | ||
| 
						 | 
					e701bcc50c | ||
| 
						 | 
					a1abf06e75 | ||
| 
						 | 
					76ace394b0 | ||
| 
						 | 
					4dac462f8f | ||
| 
						 | 
					039672b1e7 | ||
| 
						 | 
					1b26ecbbf5 | ||
| 
						 | 
					f4a7e96943 | ||
| 
						 | 
					a45bc2954f | ||
| 
						 | 
					62f32467b7 | ||
| 
						 | 
					600a1bf201 | ||
| 
						 | 
					7e196e7aa6 | ||
| 
						 | 
					9ad3507b66 | ||
| 
						 | 
					add1bdfcf6 | ||
| 
						 | 
					7ea8a7c079 | ||
| 
						 | 
					28e31f5165 | ||
| 
						 | 
					10a6975c5d | ||
| 
						 | 
					b34ea87660 | ||
| 
						 | 
					86ed69c50b | ||
| 
						 | 
					02e824154c | ||
| 
						 | 
					dc973c8491 | ||
| 
						 | 
					0a28e3a8d3 | ||
| 
						 | 
					7a48c260e1 | ||
| 
						 | 
					c55c49a3a2 | ||
| 
						 | 
					0fc9b06d12 | ||
| 
						 | 
					d71ad5a6bf | ||
| 
						 | 
					f0b3028306 | ||
| 
						 | 
					752992c527 | ||
| 
						 | 
					43c4476396 | ||
| 
						 | 
					f8172bed56 | ||
| 
						 | 
					c1f71b4cfc | ||
| 
						 | 
					4799da15e7 | ||
| 
						 | 
					4936e47c7a | ||
| 
						 | 
					cb965373aa | ||
| 
						 | 
					654a91184a | ||
| 
						 | 
					57997f7f4b | ||
| 
						 | 
					ca2f5be3d1 | ||
| 
						 | 
					1b091073f1 | ||
| 
						 | 
					85d2d3c442 | ||
| 
						 | 
					d77dfd63cf | ||
| 
						 | 
					db2bf52fca | ||
| 
						 | 
					2c62d97440 | ||
| 
						 | 
					53b2e64214 | ||
| 
						 | 
					ccf82a3ee5 | ||
| 
						 | 
					9cb6f35e60 | ||
| 
						 | 
					da2e33a040 | ||
| 
						 | 
					f2d260bfa4 | ||
| 
						 | 
					783c7244f9 | ||
| 
						 | 
					c9287e0e63 | ||
| 
						 | 
					69b84d115c | ||
| 
						 | 
					1745b898b1 | ||
| 
						 | 
					5ba4c85249 | ||
| 
						 | 
					03c87bb46d | ||
| 
						 | 
					dff5a76e9e | ||
| 
						 | 
					50386f4ca4 | ||
| 
						 | 
					de6ba2ec80 | ||
| 
						 | 
					ce126015d9 | ||
| 
						 | 
					f2337c3d43 | ||
| 
						 | 
					41b59d322a | ||
| 
						 | 
					dfea7c111e | ||
| 
						 | 
					3d5d672297 | ||
| 
						 | 
					e4dcc6f342 | ||
| 
						 | 
					9847249611 | ||
| 
						 | 
					19742a54ec | ||
| 
						 | 
					5af9ae7a80 | ||
| 
						 | 
					78962a4a75 | ||
| 
						 | 
					9a35d826aa | ||
| 
						 | 
					c9cdc89d35 | ||
| 
						 | 
					33b75703ba | ||
| 
						 | 
					ca55aa3a24 | ||
| 
						 | 
					95307c3902 | ||
| 
						 | 
					89c8eae7e2 | ||
| 
						 | 
					91614772dc | ||
| 
						 | 
					cd497ab4dc | ||
| 
						 | 
					f81895905e | ||
| 
						 | 
					d82fafaa7a | ||
| 
						 | 
					0ea01acc46 | ||
| 
						 | 
					df55792a68 | ||
| 
						 | 
					6a19c45269 | ||
| 
						 | 
					23ee345441 | ||
| 
						 | 
					0e8ff253f3 | ||
| 
						 | 
					89bddb0bc4 | ||
| 
						 | 
					77bbca9bb3 | ||
| 
						 | 
					1acd4d8d58 | ||
| 
						 | 
					4db72a712d | ||
| 
						 | 
					da2b7ba08e | ||
| 
						 | 
					f8b49411bf | ||
| 
						 | 
					a04b0d99b0 | ||
| 
						 | 
					9e9735f617 | ||
| 
						 | 
					f906b36f01 | ||
| 
						 | 
					a9d1f4f854 | ||
| 
						 | 
					d017ae905f | ||
| 
						 | 
					bede280507 | ||
| 
						 | 
					ea130047bc | ||
| 
						 | 
					d77d8de63e | ||
| 
						 | 
					f5413d9202 | ||
| 
						 | 
					559fc8f216 | ||
| 
						 | 
					34b2df911d | ||
| 
						 | 
					3d5a8a8cbd | ||
| 
						 | 
					2b07097e14 | ||
| 
						 | 
					372e943d8b | ||
| 
						 | 
					95afcedc4f | ||
| 
						 | 
					c1dc8f017c | ||
| 
						 | 
					dbeddca263 | ||
| 
						 | 
					e65d133dec | ||
| 
						 | 
					b5e1dea1c2 | ||
| 
						 | 
					3f19469939 | ||
| 
						 | 
					87874b037a | ||
| 
						 | 
					adda6841d1 | ||
| 
						 | 
					a8092b921b | ||
| 
						 | 
					930e4d69d1 | ||
| 
						 | 
					d9219df45b | ||
| 
						 | 
					a6d4ec28db | ||
| 
						 | 
					b29e13f777 | ||
| 
						 | 
					9363b78ff6 | ||
| 
						 | 
					eba411fec7 | ||
| 
						 | 
					9afe99e134 | ||
| 
						 | 
					6678856178 | ||
| 
						 | 
					a1fea38c2c | ||
| 
						 | 
					2d3265e66d | ||
| 
						 | 
					f6d4400db9 | ||
| 
						 | 
					6d892a9266 | ||
| 
						 | 
					356fa08da5 | ||
| 
						 | 
					2225f8c890 | ||
| 
						 | 
					d1a89739f9 | ||
| 
						 | 
					6ec6896763 | ||
| 
						 | 
					9fb718d610 | ||
| 
						 | 
					c913b89a47 | ||
| 
						 | 
					58b45693b7 | ||
| 
						 | 
					29d3ece9b1 | ||
| 
						 | 
					7e5db75230 | ||
| 
						 | 
					283c0b9845 | ||
| 
						 | 
					071a0f1651 | ||
| 
						 | 
					1a116557a4 | ||
| 
						 | 
					1468297626 | ||
| 
						 | 
					87e94fdf52 | ||
| 
						 | 
					64e15c33c8 | ||
| 
						 | 
					7170f435d1 | ||
| 
						 | 
					9942be0493 | ||
| 
						 | 
					eaad36277f | ||
| 
						 | 
					37cc498568 | ||
| 
						 | 
					8a3191340a | ||
| 
						 | 
					0f4e998f0a | ||
| 
						 | 
					0c93b649ba | ||
| 
						 | 
					947d269877 | ||
| 
						 | 
					b4b9453948 | ||
| 
						 | 
					b99a4cd095 | ||
| 
						 | 
					a363d5aa46 | ||
| 
						 | 
					ae442dabb4 | ||
| 
						 | 
					1477c88397 | ||
| 
						 | 
					4172462938 | ||
| 
						 | 
					27341ba4f8 | ||
| 
						 | 
					7443076094 | ||
| 
						 | 
					a764bb4b1d | ||
| 
						 | 
					57130e1af7 | ||
| 
						 | 
					3f8a354c99 | ||
| 
						 | 
					0b10d92929 | ||
| 
						 | 
					1a1c55e661 | ||
| 
						 | 
					87768f9e8b | ||
| 
						 | 
					d079f50364 | ||
| 
						 | 
					72a89835c5 | ||
| 
						 | 
					39d686810b | ||
| 
						 | 
					d936626001 | ||
| 
						 | 
					cdce1c5ea6 | ||
| 
						 | 
					d59530a54f | ||
| 
						 | 
					fe480811fb | ||
| 
						 | 
					2e3e0d34c3 | ||
| 
						 | 
					1e2984d0d9 | ||
| 
						 | 
					3e9fe693c7 | ||
| 
						 | 
					63161838d8 | ||
| 
						 | 
					7c92951ae9 | ||
| 
						 | 
					59a25854ef | ||
| 
						 | 
					3ca5768974 | ||
| 
						 | 
					9a2dce43dd | ||
| 
						 | 
					b7fce4317b | ||
| 
						 | 
					97d9514b00 | ||
| 
						 | 
					58f88708fa | ||
| 
						 | 
					d79c874aa5 | ||
| 
						 | 
					58917c1ff8 | ||
| 
						 | 
					13b171a62d | ||
| 
						 | 
					f0a601922a | ||
| 
						 | 
					d90090b35e | ||
| 
						 | 
					094d1142e7 | ||
| 
						 | 
					eb09d1c925 | ||
| 
						 | 
					bd5d75361e | ||
| 
						 | 
					5d7c0c635b | ||
| 
						 | 
					606f538bf3 | ||
| 
						 | 
					574c0eea27 | ||
| 
						 | 
					4730cbc543 | ||
| 
						 | 
					5c63359550 | ||
| 
						 | 
					ec3607f779 | ||
| 
						 | 
					7efd0f8098 | ||
| 
						 | 
					2a7c30a21a | ||
| 
						 | 
					2639c3ee3f | ||
| 
						 | 
					547e0153cb | ||
| 
						 | 
					423664fcfa | ||
| 
						 | 
					c986c230ef | ||
| 
						 | 
					7c6001f76e | ||
| 
						 | 
					d3620946ac | ||
| 
						 | 
					479643221c | ||
| 
						 | 
					c9ef8bcf6a | ||
| 
						 | 
					d655a7c2f6 | ||
| 
						 | 
					306861d3b4 | ||
| 
						 | 
					7abab41eed | ||
| 
						 | 
					acc2691e33 | ||
| 
						 | 
					21a65956b0 | ||
| 
						 | 
					9738275ad1 | ||
| 
						 | 
					7265034703 | ||
| 
						 | 
					9bcc45591d | ||
| 
						 | 
					38d657ba0e | ||
| 
						 | 
					6a0d16c5bb | ||
| 
						 | 
					490d4abb56 | ||
| 
						 | 
					a7d0f7fc99 | ||
| 
						 | 
					0d2d3ff528 | ||
| 
						 | 
					526169f0aa | ||
| 
						 | 
					42940ff0e3 | ||
| 
						 | 
					e9b943363d | ||
| 
						 | 
					01e98ff10c | ||
| 
						 | 
					d38981a17d | ||
| 
						 | 
					7612e91691 | ||
| 
						 | 
					76b0f20033 | ||
| 
						 | 
					b78c0f8a29 | ||
| 
						 | 
					ea93c7c8fc | ||
| 
						 | 
					8ac1952974 | ||
| 
						 | 
					b4514a5bbf | ||
| 
						 | 
					568053b06d | ||
| 
						 | 
					bb180bfc39 | ||
| 
						 | 
					5ee635caed | ||
| 
						 | 
					5dfd7c8597 | ||
| 
						 | 
					fe61b75d7e | ||
| 
						 | 
					e7bf565418 | ||
| 
						 | 
					12071f6251 | ||
| 
						 | 
					72ed2e3ed0 | ||
| 
						 | 
					d7c1f134fe | ||
| 
						 | 
					5072d9f7ad | ||
| 
						 | 
					5d42a1247a | ||
| 
						 | 
					ce64be22bd | ||
| 
						 | 
					36f9386e8d | ||
| 
						 | 
					e9dc963805 | ||
| 
						 | 
					8854322997 | ||
| 
						 | 
					6018f1e8ca | ||
| 
						 | 
					68f8ebb543 | ||
| 
						 | 
					301f0218fd | ||
| 
						 | 
					a9c4a56893 | ||
| 
						 | 
					3ea3d72f11 | ||
| 
						 | 
					33da5bf4a7 | ||
| 
						 | 
					e3de01948a | ||
| 
						 | 
					3cd92aaf54 | ||
| 
						 | 
					ab4e1996dd | ||
| 
						 | 
					b9ef082abf | ||
| 
						 | 
					dac7186da3 | ||
| 
						 | 
					1fd6cc2b5e | ||
| 
						 | 
					0b7da9790a | ||
| 
						 | 
					a9e0e0ead6 | ||
| 
						 | 
					079754f1a9 | ||
| 
						 | 
					c45b5b1e86 | ||
| 
						 | 
					2115cc685c | ||
| 
						 | 
					b99e56fc19 | ||
| 
						 | 
					74c057eb84 | ||
| 
						 | 
					e8ececc0dc | ||
| 
						 | 
					ad120b7736 | ||
| 
						 | 
					61c0711e79 | ||
| 
						 | 
					81c3f6fce0 | ||
| 
						 | 
					0a4416e633 | ||
| 
						 | 
					6bb712854d | ||
| 
						 | 
					46959e2407 | ||
| 
						 | 
					debbb049b0 | ||
| 
						 | 
					375377fef9 | ||
| 
						 | 
					d5758dd6a3 | ||
| 
						 | 
					d5deffea58 | ||
| 
						 | 
					fadb43d9a1 | ||
| 
						 | 
					044b641df0 | ||
| 
						 | 
					fbbacdb2ff | ||
| 
						 | 
					4bfd7acd96 | ||
| 
						 | 
					ff03225cf4 | ||
| 
						 | 
					8e820c0e47 | ||
| 
						 | 
					d718314324 | ||
| 
						 | 
					1c653ec1b1 | ||
| 
						 | 
					fe16ddadce | ||
| 
						 | 
					7a978054cc | ||
| 
						 | 
					8ed3b5a747 | ||
| 
						 | 
					6885150800 | ||
| 
						 | 
					35d33a1c7d | ||
| 
						 | 
					93a16bbd3c | ||
| 
						 | 
					d3cf6a4985 | ||
| 
						 | 
					8ef7200c32 | ||
| 
						 | 
					e43da5ba86 | ||
| 
						 | 
					10428b4340 | ||
| 
						 | 
					43b7c05cd7 | ||
| 
						 | 
					057f64b04c | ||
| 
						 | 
					0971d783f3 | ||
| 
						 | 
					54aabad3e8 | ||
| 
						 | 
					606fbb5fec | ||
| 
						 | 
					5093c27ba5 | ||
| 
						 | 
					1d3661c946 | ||
| 
						 | 
					304f7a068d | ||
| 
						 | 
					7ea91be3fc | ||
| 
						 | 
					d15464b1ae | ||
| 
						 | 
					150435f24e | ||
| 
						 | 
					e49507cd14 | ||
| 
						 | 
					ad3cc3ebb6 | ||
| 
						 | 
					ecaf53836d | ||
| 
						 | 
					8fc36e7b24 | ||
| 
						 | 
					1dbbac2aa3 | ||
| 
						 | 
					4e5775fde4 | ||
| 
						 | 
					9d81c7c366 | ||
| 
						 | 
					2df0441f0f | ||
| 
						 | 
					4216002ce5 | ||
| 
						 | 
					1af6f4e151 | ||
| 
						 | 
					05f284bc11 | ||
| 
						 | 
					40c19b3ad4 | ||
| 
						 | 
					d1d972cca0 | ||
| 
						 | 
					2a969921d6 | ||
| 
						 | 
					4b3e1f5187 | ||
| 
						 | 
					561963aa26 | ||
| 
						 | 
					6c2c192836 | ||
| 
						 | 
					f0730f2295 | ||
| 
						 | 
					9df91d2b63 | ||
| 
						 | 
					09da1941fd | ||
| 
						 | 
					613c8e4c28 | ||
| 
						 | 
					7b407fd34d | ||
| 
						 | 
					fb7ff7a803 | ||
| 
						 | 
					0c1f0e0522 | ||
| 
						 | 
					b5d771e9aa | ||
| 
						 | 
					2f64255895 | ||
| 
						 | 
					5b629a5c40 | ||
| 
						 | 
					b749cf52c3 | ||
| 
						 | 
					b9a36a9223 | ||
| 
						 | 
					32debf6e6b | ||
| 
						 | 
					06f6ae01c9 | ||
| 
						 | 
					db4d8ad802 | ||
| 
						 | 
					9c82de9c9e | ||
| 
						 | 
					8329835528 | ||
| 
						 | 
					8e624aa31b | ||
| 
						 | 
					a2e8741517 | ||
| 
						 | 
					3b7c107e8f | ||
| 
						 | 
					558bc3f971 | ||
| 
						 | 
					90a326f380 | ||
| 
						 | 
					867dc1ae35 | ||
| 
						 | 
					3f555464f7 | ||
| 
						 | 
					c0c0782258 | ||
| 
						 | 
					377f57bcb6 | ||
| 
						 | 
					a51253f1e5 | ||
| 
						 | 
					e674e94da6 | ||
| 
						 | 
					93a415f6d3 | ||
| 
						 | 
					9364b97692 | ||
| 
						 | 
					030915c9ea | ||
| 
						 | 
					10882c309a | ||
| 
						 | 
					8232fde160 | ||
| 
						 | 
					45c3474d4d | ||
| 
						 | 
					c5a9e85343 | ||
| 
						 | 
					8c7ef59583 | ||
| 
						 | 
					36404b5c92 | ||
| 
						 | 
					9949a117be | ||
| 
						 | 
					a3896db4f5 | ||
| 
						 | 
					f668062cab | ||
| 
						 | 
					bac0721078 | ||
| 
						 | 
					e29d7244d8 | ||
| 
						 | 
					5a34b91a7a | ||
| 
						 | 
					eaac8b1428 | ||
| 
						 | 
					6c6275dc88 | ||
| 
						 | 
					bd350ae679 | ||
| 
						 | 
					cadee28c68 | ||
| 
						 | 
					3534828a99 | ||
| 
						 | 
					e1c5777e25 | ||
| 
						 | 
					63553d4dd3 | ||
| 
						 | 
					6033d779fc | ||
| 
						 | 
					97db27e61f | ||
| 
						 | 
					8ed861c05f | ||
| 
						 | 
					4d416657bf | ||
| 
						 | 
					ede0399275 | ||
| 
						 | 
					1a28ada04c | ||
| 
						 | 
					8ac771cde4 | ||
| 
						 | 
					c6afbbfc73 | ||
| 
						 | 
					5ba71e8109 | ||
| 
						 | 
					a0d2922aca | ||
| 
						 | 
					446610b9df | ||
| 
						 | 
					4905eea503 | ||
| 
						 | 
					519fc128f6 | ||
| 
						 | 
					cc10955483 | ||
| 
						 | 
					e0bd47bb87 | ||
| 
						 | 
					fb0c3c4a06 | ||
| 
						 | 
					ceef1fdaf2 | ||
| 
						 | 
					e1ac287ed1 | ||
| 
						 | 
					29cd3a5945 | ||
| 
						 | 
					cab082166b | ||
| 
						 | 
					711dfd4da9 | ||
| 
						 | 
					643948331d | ||
| 
						 | 
					119030f1a5 | ||
| 
						 | 
					3efee3dc52 | ||
| 
						 | 
					70716d5121 | ||
| 
						 | 
					a7d11ead62 | ||
| 
						 | 
					017830e433 | ||
| 
						 | 
					bceb175c75 | ||
| 
						 | 
					98277c6b71 | ||
| 
						 | 
					b227fbe523 | ||
| 
						 | 
					ac6659a293 | ||
| 
						 | 
					30a24ced1d | ||
| 
						 | 
					3d1f4ae2ed | ||
| 
						 | 
					e86aff049c | ||
| 
						 | 
					8ec0ba7c9e | ||
| 
						 | 
					c2898f0b2e | ||
| 
						 | 
					0747374378 | ||
| 
						 | 
					a0c2fea833 | ||
| 
						 | 
					811c1a445f | ||
| 
						 | 
					72d3041e7e | ||
| 
						 | 
					503180e7ec | ||
| 
						 | 
					8ad548cad7 | ||
| 
						 | 
					85d9acf605 | ||
| 
						 | 
					fc93c5ffcb | ||
| 
						 | 
					d6b0a9314c | ||
| 
						 | 
					83bc07c3ff | ||
| 
						 | 
					6ca680cd41 | ||
| 
						 | 
					2b533dcb2b | ||
| 
						 | 
					13a5a09f98 | ||
| 
						 | 
					75ff7ebdbe | ||
| 
						 | 
					6d8f3a1fc8 | ||
| 
						 | 
					a11b411912 | ||
| 
						 | 
					2dba817c69 | ||
| 
						 | 
					d4df9a545f | ||
| 
						 | 
					cae21391e5 | ||
| 
						 | 
					573b22d2c2 | ||
| 
						 | 
					14d4112a9d | ||
| 
						 | 
					28aa9db9c5 | ||
| 
						 | 
					d50c349e00 | ||
| 
						 | 
					905362c59c | ||
| 
						 | 
					97e7d44310 | ||
| 
						 | 
					bb71a1ef23 | ||
| 
						 | 
					de2771d008 | ||
| 
						 | 
					02de34b65a | ||
| 
						 | 
					c6e14889f2 | ||
| 
						 | 
					f843732790 | ||
| 
						 | 
					80e7ff0fc1 | ||
| 
						 | 
					2964db6bdd | ||
| 
						 | 
					5fbd94ec74 | ||
| 
						 | 
					1d8d31d4f0 | ||
| 
						 | 
					707c3eed99 | ||
| 
						 | 
					e33a81ad36 | ||
| 
						 | 
					0f7d216379 | ||
| 
						 | 
					f0052e97a6 | ||
| 
						 | 
					5c6076d48d | ||
| 
						 | 
					883bd31249 | ||
| 
						 | 
					d7b5fde3c7 | ||
| 
						 | 
					f4273efef0 | ||
| 
						 | 
					471dc966d9 | ||
| 
						 | 
					50368654a4 | ||
| 
						 | 
					b1e1cc04ce | ||
| 
						 | 
					554f67e295 | ||
| 
						 | 
					278bd09a6c | ||
| 
						 | 
					300d083038 | ||
| 
						 | 
					34664f0025 | ||
| 
						 | 
					7aaa803813 | ||
| 
						 | 
					b075b3ec1c | ||
| 
						 | 
					0f3f4b0f5b | ||
| 
						 | 
					fd5ed78442 | ||
| 
						 | 
					d516e7dcb4 | ||
| 
						 | 
					6a1f1ebf9d | ||
| 
						 | 
					6ba5c02dd6 | ||
| 
						 | 
					c9b8f90859 | ||
| 
						 | 
					70a0bf6d5c | ||
| 
						 | 
					4caf8d6fab | ||
| 
						 | 
					aa90593158 | ||
| 
						 | 
					9da5531483 | ||
| 
						 | 
					9483459ba7 | ||
| 
						 | 
					ae57883770 | ||
| 
						 | 
					8cb5a89d10 | ||
| 
						 | 
					1a35fb0d4d | ||
| 
						 | 
					1a2483c68c | ||
| 
						 | 
					6c806238b2 | ||
| 
						 | 
					ea6e07a38b | ||
| 
						 | 
					ec6b07b275 | ||
| 
						 | 
					e85a03a1ca | ||
| 
						 | 
					5d66b7677f | ||
| 
						 | 
					3932834172 | ||
| 
						 | 
					d2e8294a1a | ||
| 
						 | 
					b06aa66ad8 | ||
| 
						 | 
					c45b48d949 | ||
| 
						 | 
					f779af1f65 | ||
| 
						 | 
					78d039cd5f | ||
| 
						 | 
					fdbd5418c7 | ||
| 
						 | 
					5df42466fd | ||
| 
						 | 
					ff2bed87f2 | ||
| 
						 | 
					765e7d0b1e | ||
| 
						 | 
					71ab6d42c8 | ||
| 
						 | 
					2ea2a662ef | ||
| 
						 | 
					c2d45349a4 | ||
| 
						 | 
					1b5cf410b7 | ||
| 
						 | 
					3e79e2bed0 | ||
| 
						 | 
					2a1f933702 | ||
| 
						 | 
					251d3b487f | ||
| 
						 | 
					e55ec48c7d | ||
| 
						 | 
					7ce2195adb | ||
| 
						 | 
					537b79f1c2 | ||
| 
						 | 
					e4c4d22f56 | ||
| 
						 | 
					c4d244398c | ||
| 
						 | 
					47260cdaf0 | ||
| 
						 | 
					7ef13058c7 | ||
| 
						 | 
					8066cb1804 | ||
| 
						 | 
					c5af6062fa | ||
| 
						 | 
					908a2eba20 | ||
| 
						 | 
					6f43957703 | ||
| 
						 | 
					897ea28cf4 | ||
| 
						 | 
					d056ddbff1 | ||
| 
						 | 
					f157350059 | ||
| 
						 | 
					7e3d99bda3 | ||
| 
						 | 
					c7c53e1d94 | ||
| 
						 | 
					b912ee3f6f | ||
| 
						 | 
					06c05ad8e5 | ||
| 
						 | 
					7f411400d5 | ||
| 
						 | 
					f24f234869 | ||
| 
						 | 
					4e384e7811 | ||
| 
						 | 
					5143448163 | ||
| 
						 | 
					8e5e9f7cbc | ||
| 
						 | 
					5ea7fd01bf | ||
| 
						 | 
					8f3f6bc774 | ||
| 
						 | 
					7f1524b42f | ||
| 
						 | 
					4b7db4689d | ||
| 
						 | 
					92831ecb43 | ||
| 
						 | 
					d486f3f306 | ||
| 
						 | 
					bfb7468f62 | ||
| 
						 | 
					2e417d41db | ||
| 
						 | 
					b17f68cbfb | ||
| 
						 | 
					8a22a523d6 | ||
| 
						 | 
					97fe752a4e | ||
| 
						 | 
					cc4ea122a2 | ||
| 
						 | 
					db995d29e5 | ||
| 
						 | 
					c22dde8008 | ||
| 
						 | 
					462d888894 | ||
| 
						 | 
					9311b914ad | ||
| 
						 | 
					c5e0488210 | ||
| 
						 | 
					f548c27179 | ||
| 
						 | 
					1f5f405b52 | ||
| 
						 | 
					cd923ab838 | ||
| 
						 | 
					aacdff92d2 | ||
| 
						 | 
					32160d388a | ||
| 
						 | 
					72e31929eb | ||
| 
						 | 
					29014bb40e | ||
| 
						 | 
					e9d0a65536 | ||
| 
						 | 
					f7a3b68834 | ||
| 
						 | 
					dcc9d3b588 | ||
| 
						 | 
					b8f6f65943 | ||
| 
						 | 
					3dacdda21a | ||
| 
						 | 
					49b766120f | ||
| 
						 | 
					2d7e1553f8 | ||
| 
						 | 
					d5d41213b0 | ||
| 
						 | 
					328df00d2e | ||
| 
						 | 
					e4a5bf8234 | ||
| 
						 | 
					d6ee0f216c | ||
| 
						 | 
					0246ef6b6a | ||
| 
						 | 
					7fd40518fd | ||
| 
						 | 
					95eb53edf6 | ||
| 
						 | 
					4cdf2962cd | ||
| 
						 | 
					5bf8ccd1fd | ||
| 
						 | 
					dd9fbbcfa7 | ||
| 
						 | 
					72d8fd5be0 | ||
| 
						 | 
					b577229ed3 | ||
| 
						 | 
					4ae870b799 | ||
| 
						 | 
					0590e74b30 | ||
| 
						 | 
					b9956ba800 | ||
| 
						 | 
					fef6259d09 | ||
| 
						 | 
					e24f19e88b | ||
| 
						 | 
					cc6de1e71c | ||
| 
						 | 
					b0240a1c95 | ||
| 
						 | 
					8acdec333b | ||
| 
						 | 
					eafcbb81f9 | ||
| 
						 | 
					4b083463c8 | ||
| 
						 | 
					78e834e04f | ||
| 
						 | 
					2ed1893620 | ||
| 
						 | 
					35f188a7dd | ||
| 
						 | 
					07f577a9dd | ||
| 
						 | 
					25c72b50c0 | ||
| 
						 | 
					e07346ebea | ||
| 
						 | 
					699b466291 | ||
| 
						 | 
					f0f24ff96f | ||
| 
						 | 
					1113997c71 | ||
| 
						 | 
					487fb6e5c9 | ||
| 
						 | 
					62908a323c | ||
| 
						 | 
					021131271a | ||
| 
						 | 
					c20fa4dbd2 | ||
| 
						 | 
					b1826678da | ||
| 
						 | 
					1630338f4e | ||
| 
						 | 
					78af3d979c | ||
| 
						 | 
					8346dfb1f5 | ||
| 
						 | 
					5ecb4e4fe4 | ||
| 
						 | 
					ec9ff23b23 | ||
| 
						 | 
					149e9931e7 | ||
| 
						 | 
					58b62094bd | ||
| 
						 | 
					d357074ad4 | ||
| 
						 | 
					d9bacc129b | ||
| 
						 | 
					28d869b099 | ||
| 
						 | 
					d04e87718a | ||
| 
						 | 
					8d81bf60c9 | ||
| 
						 | 
					4fe9094ff7 | ||
| 
						 | 
					4cfde45755 | ||
| 
						 | 
					c28844dbeb | ||
| 
						 | 
					837ea7c8d6 | ||
| 
						 | 
					6c85ac8827 | ||
| 
						 | 
					c8eb8bf35d | ||
| 
						 | 
					0f6cbddd57 | ||
| 
						 | 
					530b4755bf | ||
| 
						 | 
					679395b3fe | ||
| 
						 | 
					9293442836 | ||
| 
						 | 
					4841b412f3 | ||
| 
						 | 
					f03575ad89 | ||
| 
						 | 
					e0e5428ebd | ||
| 
						 | 
					015f7edae2 | ||
| 
						 | 
					d5414f5d91 | ||
| 
						 | 
					4bc3512d42 | ||
| 
						 | 
					4c305994ca | ||
| 
						 | 
					788e2cba04 | ||
| 
						 | 
					b79b79970e | ||
| 
						 | 
					8b0f4de516 | ||
| 
						 | 
					a17000ad6d | ||
| 
						 | 
					c9f63c58df | ||
| 
						 | 
					c311b1c706 | ||
| 
						 | 
					072451afae | ||
| 
						 | 
					7349793463 | ||
| 
						 | 
					c0144eab86 | ||
| 
						 | 
					ba79b25944 | ||
| 
						 | 
					3fb34b12a0 | ||
| 
						 | 
					b6c17e76c2 | ||
| 
						 | 
					1201f20d79 | ||
| 
						 | 
					98ae8e692e | ||
| 
						 | 
					8342dbff4e | ||
| 
						 | 
					a22aa0f9a7 | ||
| 
						 | 
					b49d221ec1 | ||
| 
						 | 
					f1be315147 | ||
| 
						 | 
					8aaac2a3d4 | ||
| 
						 | 
					bd07d30e47 | ||
| 
						 | 
					54e103f00f | ||
| 
						 | 
					5404d8f7ab | ||
| 
						 | 
					1364e02c67 | ||
| 
						 | 
					b4818e2f9a | ||
| 
						 | 
					734cf57d4a | ||
| 
						 | 
					9236610ec1 | ||
| 
						 | 
					c33828a5d9 | ||
| 
						 | 
					faadb03e46 | ||
| 
						 | 
					3a0c4c51d2 | ||
| 
						 | 
					6c28c2b91e | ||
| 
						 | 
					5226df6194 | ||
| 
						 | 
					1ad23a3cbb | ||
| 
						 | 
					c4149ca304 | ||
| 
						 | 
					68b02fe950 | ||
| 
						 | 
					d9408523a5 | ||
| 
						 | 
					40ad0f063a | ||
| 
						 | 
					40d85e8926 | ||
| 
						 | 
					e655f719e6 | ||
| 
						 | 
					ede0ea02c5 | ||
| 
						 | 
					e579575b21 | ||
| 
						 | 
					06ffcc9fdf | ||
| 
						 | 
					774d03e510 | ||
| 
						 | 
					adcac9dfe6 | ||
| 
						 | 
					399e14c70c | ||
| 
						 | 
					ec8b51033b | ||
| 
						 | 
					ca5d167b6a | ||
| 
						 | 
					fcd1b001d1 | ||
| 
						 | 
					1cabbefe04 | ||
| 
						 | 
					ecefe22c95 | ||
| 
						 | 
					64db77b9a2 | ||
| 
						 | 
					19d7360ad5 | ||
| 
						 | 
					2f014cd827 | ||
| 
						 | 
					7fb84205d9 | ||
| 
						 | 
					47e1127c5f | ||
| 
						 | 
					e036b59306 | ||
| 
						 | 
					f4904c3b53 | ||
| 
						 | 
					3002a63ba5 | ||
| 
						 | 
					b69717e6c3 | ||
| 
						 | 
					d07962953f | ||
| 
						 | 
					857cf0d21e | ||
| 
						 | 
					5f08c2615d | ||
| 
						 | 
					e87428ef33 | ||
| 
						 | 
					03fd54fe70 | ||
| 
						 | 
					086c2c8253 | ||
| 
						 | 
					05c19a32ea | ||
| 
						 | 
					99d174906a | ||
| 
						 | 
					82e30a326a | ||
| 
						 | 
					2963a9cdca | ||
| 
						 | 
					288da75b2b | ||
| 
						 | 
					0943a496dd | ||
| 
						 | 
					e5dd7cc2fa | ||
| 
						 | 
					358836ef9f | ||
| 
						 | 
					aab4fac6c5 | ||
| 
						 | 
					525540b603 | ||
| 
						 | 
					b30eeb4694 | ||
| 
						 | 
					3faf0aa2fc | ||
| 
						 | 
					fd728dec5d | ||
| 
						 | 
					08b14b72d4 | ||
| 
						 | 
					de2e005abf | ||
| 
						 | 
					0fc75239a6 | ||
| 
						 | 
					390fe30a0d | ||
| 
						 | 
					633f49fcd2 | ||
| 
						 | 
					30c0ba93b9 | ||
| 
						 | 
					e935fb9621 | ||
| 
						 | 
					5ce8bb1d08 | ||
| 
						 | 
					1acd12980a | ||
| 
						 | 
					683235dd8a | ||
| 
						 | 
					65fe183ad4 | ||
| 
						 | 
					f39d5d355c | ||
| 
						 | 
					eccc8e0ff0 | ||
| 
						 | 
					dd4d8e775c | ||
| 
						 | 
					7a7f857b2f | ||
| 
						 | 
					10882b7d93 | ||
| 
						 | 
					e669b81005 | ||
| 
						 | 
					28a81d9539 | ||
| 
						 | 
					6bf5e4a6b8 | ||
| 
						 | 
					a51eee93f4 | ||
| 
						 | 
					bece2555c2 | ||
| 
						 | 
					d98d405009 | ||
| 
						 | 
					8d0881632c | ||
| 
						 | 
					e8b81da897 | ||
| 
						 | 
					acf4fbf750 | ||
| 
						 | 
					b4f1921d33 | ||
| 
						 | 
					94b4816f53 | ||
| 
						 | 
					b6ddafde3e | ||
| 
						 | 
					b01036818f | ||
| 
						 | 
					ad7da1a0c3 | ||
| 
						 | 
					1e0818d9d9 | ||
| 
						 | 
					a4686f01c3 | ||
| 
						 | 
					84d76f9aab | ||
| 
						 | 
					b90bf5eb86 | ||
| 
						 | 
					fe258f3fe5 | ||
| 
						 | 
					d339494594 | ||
| 
						 | 
					adfaf13055 | ||
| 
						 | 
					8abfeb5923 | ||
| 
						 | 
					c1a027a771 | ||
| 
						 | 
					5f3094d79b | ||
| 
						 | 
					925b81aca8 | ||
| 
						 | 
					c0c8437966 | ||
| 
						 | 
					1d27b2fc4a | ||
| 
						 | 
					5da43e7808 | ||
| 
						 | 
					5edbb558ae | ||
| 
						 | 
					7347cc1df2 | ||
| 
						 | 
					20cac11b2a | ||
| 
						 | 
					75c35c4ff8 | ||
| 
						 | 
					9d573512d0 | ||
| 
						 | 
					7921365853 | ||
| 
						 | 
					43263fd3b9 | ||
| 
						 | 
					59042a5ead | ||
| 
						 | 
					91b14de807 | ||
| 
						 | 
					81fab2be08 | ||
| 
						 | 
					165b742782 | ||
| 
						 | 
					76fef9c807 | ||
| 
						 | 
					e69ea0b9dc | ||
| 
						 | 
					98d3183f2b | ||
| 
						 | 
					a29390a951 | ||
| 
						 | 
					6291ce8617 | ||
| 
						 | 
					c76b1b50a0 | ||
| 
						 | 
					cc45e2aec0 | ||
| 
						 | 
					17efebb8e8 | ||
| 
						 | 
					5c94c733ee | ||
| 
						 | 
					156b89dd9c | ||
| 
						 | 
					34ba9f67e7 | ||
| 
						 | 
					5ddaa6b872 | ||
| 
						 | 
					9043fa7f56 | ||
| 
						 | 
					4c8e487dc9 | ||
| 
						 | 
					d3b87179aa | ||
| 
						 | 
					2166de8331 | ||
| 
						 | 
					f0bc3f001f | ||
| 
						 | 
					50448e7085 | ||
| 
						 | 
					cd1d42353e | ||
| 
						 | 
					5e588bf737 | ||
| 
						 | 
					7ff777d178 | ||
| 
						 | 
					861621189a | ||
| 
						 | 
					dcc00e08fd | ||
| 
						 | 
					a1b8a47d4b | ||
| 
						 | 
					1fd6b5e239 | ||
| 
						 | 
					f91e45bf44 | ||
| 
						 | 
					0675a45592 | ||
| 
						 | 
					0c7c6ae451 | ||
| 
						 | 
					cf089e8c4c | ||
| 
						 | 
					90928ac679 | ||
| 
						 | 
					3bbcf71784 | ||
| 
						 | 
					222734775d | ||
| 
						 | 
					f23ee7a6e0 | ||
| 
						 | 
					5b075aa6d5 | ||
| 
						 | 
					80cd6b693e | ||
| 
						 | 
					04721a12b1 | ||
| 
						 | 
					64e22c0e46 | ||
| 
						 | 
					d5a70c5b08 | ||
| 
						 | 
					eaac7b6bcf | ||
| 
						 | 
					b062a491cd | ||
| 
						 | 
					1e868517bb | ||
| 
						 | 
					7b2a93a2d7 | ||
| 
						 | 
					f57f0447c6 | ||
| 
						 | 
					7126ff881e | ||
| 
						 | 
					e28da4b165 | ||
| 
						 | 
					92d9b91f7c | ||
| 
						 | 
					149c4a30c0 | ||
| 
						 | 
					84e62062ec | ||
| 
						 | 
					dc1fb74850 | ||
| 
						 | 
					00c6010789 | ||
| 
						 | 
					5d35c058e0 | ||
| 
						 | 
					1522a521f6 | ||
| 
						 | 
					6e11b885f9 | ||
| 
						 | 
					442ae6e0e8 | ||
| 
						 | 
					6b49e83464 | ||
| 
						 | 
					f1ecf13fe1 | ||
| 
						 | 
					83d1c8582b | ||
| 
						 | 
					179f6cd454 | ||
| 
						 | 
					91b1474ff0 | ||
| 
						 | 
					15aabc88a1 | ||
| 
						 | 
					afc0d3017d | ||
| 
						 | 
					9e0b1dc8aa | ||
| 
						 | 
					55b482fd26 | ||
| 
						 | 
					4f9c9a6566 | ||
| 
						 | 
					4725120ee9 | ||
| 
						 | 
					a288f50fbb | ||
| 
						 | 
					09cf2560a4 | ||
| 
						 | 
					16353de7b1 | ||
| 
						 | 
					eb5834cb5c | ||
| 
						 | 
					3ef5736aed | ||
| 
						 | 
					d2e7b77d16 | ||
| 
						 | 
					8f794bce75 | ||
| 
						 | 
					c33196a50a | ||
| 
						 | 
					6e8b5b431f | ||
| 
						 | 
					8ca3ecf17f | ||
| 
						 | 
					91f7db59ea | ||
| 
						 | 
					a0b4501352 | ||
| 
						 | 
					345047820a | ||
| 
						 | 
					ac3525a953 | ||
| 
						 | 
					c8c3f5b134 | ||
| 
						 | 
					51319c0718 | ||
| 
						 | 
					43399b8b47 | ||
| 
						 | 
					a6596042b7 | ||
| 
						 | 
					23ae85fc9c | ||
| 
						 | 
					2da54862f1 | ||
| 
						 | 
					f272fb0559 | ||
| 
						 | 
					bd04e33586 | ||
| 
						 | 
					b09b9752ca | ||
| 
						 | 
					a810a48158 | ||
| 
						 | 
					b4f5792aa8 | ||
| 
						 | 
					fdf0330b4f | ||
| 
						 | 
					ca73743082 | ||
| 
						 | 
					df0cde2cfd | ||
| 
						 | 
					5a8421e807 | ||
| 
						 | 
					025ac95d81 | ||
| 
						 | 
					71b5824fdc | ||
| 
						 | 
					1a907c0be7 | ||
| 
						 | 
					1635cabd32 | ||
| 
						 | 
					27946a5b0f | ||
| 
						 | 
					e141822fd0 | ||
| 
						 | 
					6326828a02 | ||
| 
						 | 
					96b5362491 | ||
| 
						 | 
					8ab91a51bc | ||
| 
						 | 
					35e7e7af18 | ||
| 
						 | 
					0e248a7302 | ||
| 
						 | 
					d5ce4d6600 | ||
| 
						 | 
					2365bb5e87 | ||
| 
						 | 
					2230216a7e | ||
| 
						 | 
					54f896d25f | ||
| 
						 | 
					e47d239caf | ||
| 
						 | 
					ac259958d7 | ||
| 
						 | 
					0505ad88a6 | ||
| 
						 | 
					b323de3383 | ||
| 
						 | 
					c0e023324b | ||
| 
						 | 
					495806d5e3 | ||
| 
						 | 
					3a9f745a2d | ||
| 
						 | 
					7b15d114a3 | ||
| 
						 | 
					6ac0f72169 | ||
| 
						 | 
					ff68030231 | ||
| 
						 | 
					d34361b6dd | ||
| 
						 | 
					56c6ebe523 | ||
| 
						 | 
					2c6d2e0eb4 | ||
| 
						 | 
					8e64196a8f | ||
| 
						 | 
					926d5faf21 | ||
| 
						 | 
					18bad2af25 | ||
| 
						 | 
					6be4dc4aac | ||
| 
						 | 
					6398a59dda | ||
| 
						 | 
					48158b1db6 | ||
| 
						 | 
					47fe519e54 | ||
| 
						 | 
					7e06f19bbc | ||
| 
						 | 
					0b66c1f0d6 | ||
| 
						 | 
					ff2742ffd8 | ||
| 
						 | 
					70c2992a5f | ||
| 
						 | 
					4ac4ee6874 | ||
| 
						 | 
					865164a404 | ||
| 
						 | 
					f9c8069ab2 | ||
| 
						 | 
					2fbf5140e5 | ||
| 
						 | 
					0126f8144a | ||
| 
						 | 
					71383d7482 | ||
| 
						 | 
					4d3bfc5f42 | ||
| 
						 | 
					c173d217de | ||
| 
						 | 
					d2da9a039f | ||
| 
						 | 
					c915edd458 | ||
| 
						 | 
					504948452e | ||
| 
						 | 
					3daa6c7a54 | ||
| 
						 | 
					10cd1580a5 | ||
| 
						 | 
					5b0876e106 | ||
| 
						 | 
					cfcdeaff01 | ||
| 
						 | 
					7c34f233fd | ||
| 
						 | 
					686146a7d9 | ||
| 
						 | 
					44732d5977 | ||
| 
						 | 
					05c3794cf7 | ||
| 
						 | 
					f0431a504a | ||
| 
						 | 
					eccec85ea6 | ||
| 
						 | 
					16ff538804 | ||
| 
						 | 
					f81e07eb6f | ||
| 
						 | 
					1cbd41ad87 | ||
| 
						 | 
					d8567b8427 | ||
| 
						 | 
					afe6a1923b | ||
| 
						 | 
					0f60f4754f | ||
| 
						 | 
					7e988d0401 | ||
| 
						 | 
					d85813c229 | ||
| 
						 | 
					12eb56809d | ||
| 
						 | 
					d792172779 | ||
| 
						 | 
					011562ae73 | ||
| 
						 | 
					71c4e36933 | ||
| 
						 | 
					6261982f6c | ||
| 
						 | 
					4ac01e0ed0 | ||
| 
						 | 
					2dfe07f13e | ||
| 
						 | 
					7f4e4017ce | ||
| 
						 | 
					635970f37b | ||
| 
						 | 
					106e740220 | ||
| 
						 | 
					3e6420ba6f | ||
| 
						 | 
					79be8010d5 | ||
| 
						 | 
					0b6e679f3b | ||
| 
						 | 
					97c43ad2b9 | ||
| 
						 | 
					cbf978745c | ||
| 
						 | 
					63dceada90 | ||
| 
						 | 
					366aade8b4 | ||
| 
						 | 
					aeb505e3d9 | ||
| 
						 | 
					f9482e4773 | ||
| 
						 | 
					57dfc22ff7 | ||
| 
						 | 
					adc489b6ed | ||
| 
						 | 
					0623684315 | ||
| 
						 | 
					68afccd859 | ||
| 
						 | 
					99eef8fb28 | ||
| 
						 | 
					47e7685d39 | ||
| 
						 | 
					b6ea596ade | ||
| 
						 | 
					44d60b469b | ||
| 
						 | 
					51087408df | ||
| 
						 | 
					96226d9e6e | ||
| 
						 | 
					28f0f62424 | ||
| 
						 | 
					2e772a8cd4 | ||
| 
						 | 
					3fd192f0cf | ||
| 
						 | 
					d16ae81961 | ||
| 
						 | 
					211a8e093e | ||
| 
						 | 
					84010a51d6 | ||
| 
						 | 
					e9700ea19b | ||
| 
						 | 
					e81ec68072 | ||
| 
						 | 
					6096e97708 | ||
| 
						 | 
					6f302b66b4 | ||
| 
						 | 
					b6e41890f7 | ||
| 
						 | 
					be5332a048 | ||
| 
						 | 
					4d72aeb0cd | ||
| 
						 | 
					c7c3c35100 | ||
| 
						 | 
					2f0df3c552 | ||
| 
						 | 
					e6effcd921 | ||
| 
						 | 
					cd462b9523 | ||
| 
						 | 
					7e2e17d38b | ||
| 
						 | 
					665d616a06 | ||
| 
						 | 
					ced90f5bb9 | ||
| 
						 | 
					ca0ddb6cb4 | ||
| 
						 | 
					3d733813e1 | ||
| 
						 | 
					45ddc0b154 | ||
| 
						 | 
					87294497f3 | ||
| 
						 | 
					5f45f9c0d0 | ||
| 
						 | 
					7f78c06793 | ||
| 
						 | 
					8ea5b7eebc | ||
| 
						 | 
					e49cd511af | ||
| 
						 | 
					15c88646df | ||
| 
						 | 
					f47919543d | ||
| 
						 | 
					a95291d9cd | ||
| 
						 | 
					033deb3d29 | ||
| 
						 | 
					923994c1f4 | ||
| 
						 | 
					3d45b839b4 | ||
| 
						 | 
					55d1efa212 | ||
| 
						 | 
					21d794a0e5 | ||
| 
						 | 
					614022a78c | ||
| 
						 | 
					c00688e23a | ||
| 
						 | 
					5c69917d19 | ||
| 
						 | 
					6a5eb75b6c | ||
| 
						 | 
					727c55eaa2 | ||
| 
						 | 
					9b03173ec5 | ||
| 
						 | 
					ab95855d6d | ||
| 
						 | 
					bb84594c6b | ||
| 
						 | 
					97392c76b1 | ||
| 
						 | 
					53aec2b306 | ||
| 
						 | 
					a0a381dc63 | ||
| 
						 | 
					4fa95edeec | ||
| 
						 | 
					b944f1d70e | ||
| 
						 | 
					0f1a5d0085 | ||
| 
						 | 
					e7e10222e7 | ||
| 
						 | 
					87c4fda588 | ||
| 
						 | 
					0ff820de6f | ||
| 
						 | 
					0dcf55e6d9 | ||
| 
						 | 
					a69842c910 | ||
| 
						 | 
					318f635e4d | ||
| 
						 | 
					abf0d72316 | ||
| 
						 | 
					5cd89c8844 | ||
| 
						 | 
					501c1af38c | ||
| 
						 | 
					8526e130d9 | ||
| 
						 | 
					76fd08eade | ||
| 
						 | 
					bfe48ae9d2 | ||
| 
						 | 
					8547cdba7c | ||
| 
						 | 
					00d0cd631d | ||
| 
						 | 
					04460d13cd | ||
| 
						 | 
					a17a15e5d7 | ||
| 
						 | 
					bfe2465658 | ||
| 
						 | 
					4568987785 | ||
| 
						 | 
					c0e5a1419d | ||
| 
						 | 
					23ac7ab748 | ||
| 
						 | 
					5622dc74a8 | ||
| 
						 | 
					528986f4f0 | ||
| 
						 | 
					e6b8ff3f91 | ||
| 
						 | 
					3fc3c5296f | ||
| 
						 | 
					f4f2a14a31 | ||
| 
						 | 
					fa3f464cb6 | ||
| 
						 | 
					bed20538ae | ||
| 
						 | 
					3da461c6e6 | ||
| 
						 | 
					9c73e8452a | ||
| 
						 | 
					750dab69d3 | ||
| 
						 | 
					702d0da47c | ||
| 
						 | 
					3a955b1e53 | ||
| 
						 | 
					d408bd93e8 | ||
| 
						 | 
					63fd9eb1de | ||
| 
						 | 
					4718ab6ddf | ||
| 
						 | 
					affd4b3a26 | ||
| 
						 | 
					6a660dbcda | ||
| 
						 | 
					5ff3af8b48 | ||
| 
						 | 
					9d49070f93 | ||
| 
						 | 
					5b3994a1dd | ||
| 
						 | 
					bb1e62c580 | ||
| 
						 | 
					29cc776908 | ||
| 
						 | 
					d2ef60b2ec | ||
| 
						 | 
					41ea496d41 | ||
| 
						 | 
					c69bb38929 | ||
| 
						 | 
					23b3f8494e | ||
| 
						 | 
					0ff94eec1b | ||
| 
						 | 
					f67a638565 | ||
| 
						 | 
					778cf71c61 | ||
| 
						 | 
					e2da083b48 | ||
| 
						 | 
					dcc48c0d4b | ||
| 
						 | 
					2446b4a591 | ||
| 
						 | 
					a973d633c8 | ||
| 
						 | 
					c3b11e6e0f | ||
| 
						 | 
					44bec2d99e | ||
| 
						 | 
					0d59d0a3f6 | ||
| 
						 | 
					ddc8372b09 | ||
| 
						 | 
					ea7325f4e0 | ||
| 
						 | 
					6048f95415 | ||
| 
						 | 
					5682705b8d | ||
| 
						 | 
					6c3b82607d | ||
| 
						 | 
					486c9f4ebf | ||
| 
						 | 
					7c8adebf2d | ||
| 
						 | 
					ce98bb0dc1 | ||
| 
						 | 
					60b49a4296 | ||
| 
						 | 
					5d0a2ffae0 | ||
| 
						 | 
					bbcf7c722a | ||
| 
						 | 
					66574eec6d | ||
| 
						 | 
					305df3a42c | ||
| 
						 | 
					323c6f4270 | ||
| 
						 | 
					c056b2b14c | ||
| 
						 | 
					02ebb07fd8 | ||
| 
						 | 
					59db0890c3 | ||
| 
						 | 
					3ea0d9ed3a | ||
| 
						 | 
					0371d11dd5 | ||
| 
						 | 
					12f1ebaee9 | ||
| 
						 | 
					5202cc22d1 | ||
| 
						 | 
					07a34bdcbf | ||
| 
						 | 
					27806da4be | ||
| 
						 | 
					a15dd1a3ce | ||
| 
						 | 
					6cc7451c7c | ||
| 
						 | 
					652aeee782 | ||
| 
						 | 
					8e1e887b8f | ||
| 
						 | 
					6601a2de64 | ||
| 
						 | 
					75a42ffc32 | ||
| 
						 | 
					8a80a52f5f | ||
| 
						 | 
					b373a434ba | ||
| 
						 | 
					cd78fdafe6 | ||
| 
						 | 
					7a1b4c6db7 | ||
| 
						 | 
					f2c7664033 | ||
| 
						 | 
					12b295f067 | ||
| 
						 | 
					3ee46da40f | ||
| 
						 | 
					beadaa7212 | ||
| 
						 | 
					16ef09426f | ||
| 
						 | 
					b8c1f1f5a9 | ||
| 
						 | 
					e027b5cbd6 | ||
| 
						 | 
					bf1bd73ad1 | ||
| 
						 | 
					86fc95119a | ||
| 
						 | 
					b3a76ea17b | ||
| 
						 | 
					bababd9d53 | ||
| 
						 | 
					8cd5180531 | ||
| 
						 | 
					f1ccbade8c | ||
| 
						 | 
					a66f4b0417 | ||
| 
						 | 
					ff495b2261 | ||
| 
						 | 
					90e5824212 | ||
| 
						 | 
					3d64021062 | ||
| 
						 | 
					fb8ed7428d | ||
| 
						 | 
					bdc273c10a | ||
| 
						 | 
					2d96cffdc7 | ||
| 
						 | 
					4356fccbcd | ||
| 
						 | 
					a169fd4ce7 | ||
| 
						 | 
					dcd418139e | ||
| 
						 | 
					0cac2062d3 | ||
| 
						 | 
					854c5d4ade | ||
| 
						 | 
					0f96c6ec84 | ||
| 
						 | 
					b219bd66c1 | ||
| 
						 | 
					cb52f2c0b3 | ||
| 
						 | 
					d3273e03ef | ||
| 
						 | 
					afbfd963f3 | ||
| 
						 | 
					2cd101c5f3 | ||
| 
						 | 
					b10bc24dee | ||
| 
						 | 
					51a23df861 | ||
| 
						 | 
					03a0a9dad9 | ||
| 
						 | 
					362c0affbe | ||
| 
						 | 
					d5b523479f | ||
| 
						 | 
					7719b8f6d7 | ||
| 
						 | 
					3656c0d524 | ||
| 
						 | 
					8b9e0dd6ea | ||
| 
						 | 
					b3921b1037 | ||
| 
						 | 
					d7d96e5dbf | ||
| 
						 | 
					3934395b7b | ||
| 
						 | 
					04fbe9a529 | ||
| 
						 | 
					badd6dd9a2 | ||
| 
						 | 
					059082456d | ||
| 
						 | 
					405b68e22f | ||
| 
						 | 
					c90bf6692c | ||
| 
						 | 
					b5ea5d0cc5 | ||
| 
						 | 
					243617a1e1 | ||
| 
						 | 
					9e703edd59 | ||
| 
						 | 
					d6c2cf2810 | ||
| 
						 | 
					ae7811acfa | ||
| 
						 | 
					e55731c099 | ||
| 
						 | 
					9df5c74da4 | ||
| 
						 | 
					8b75f9f785 | ||
| 
						 | 
					bf12428cf4 | ||
| 
						 | 
					b551df978b | ||
| 
						 | 
					c91d85d2f0 | ||
| 
						 | 
					965237fa1f | ||
| 
						 | 
					24cd9afc06 | ||
| 
						 | 
					602fdefebd | ||
| 
						 | 
					7e7818aa17 | ||
| 
						 | 
					27a6023a6a | ||
| 
						 | 
					3861879900 | ||
| 
						 | 
					807eeb8351 | ||
| 
						 | 
					045fa53d58 | ||
| 
						 | 
					69d405ece7 | ||
| 
						 | 
					d3dc4d0c5b | ||
| 
						 | 
					7beba1188e | ||
| 
						 | 
					9779ebe12c | ||
| 
						 | 
					f72cfaa093 | ||
| 
						 | 
					cf8ccafb4a | ||
| 
						 | 
					7fe281b0b8 | ||
| 
						 | 
					5f70e9574f | ||
| 
						 | 
					4be51923ba | ||
| 
						 | 
					a72c78f4a4 | ||
| 
						 | 
					b4d32a6de1 | ||
| 
						 | 
					8a6b2d5daa | ||
| 
						 | 
					bebadeef14 | ||
| 
						 | 
					3551be67f0 | ||
| 
						 | 
					1103950b25 | ||
| 
						 | 
					9d06e01cec | ||
| 
						 | 
					24be946b7e | ||
| 
						 | 
					f56d2fc60f | ||
| 
						 | 
					9f8356f409 | ||
| 
						 | 
					8e6345d938 | ||
| 
						 | 
					0eac78bd1c | ||
| 
						 | 
					21db2502d0 | ||
| 
						 | 
					38c3492123 | ||
| 
						 | 
					6376bff476 | ||
| 
						 | 
					38495a3ebc | ||
| 
						 | 
					cf00897be2 | ||
| 
						 | 
					da640f24ec | ||
| 
						 | 
					ed6a777c42 | ||
| 
						 | 
					81af4485d3 | ||
| 
						 | 
					94d8f0237d | ||
| 
						 | 
					c5e579dd38 | ||
| 
						 | 
					7865e76ee2 | ||
| 
						 | 
					4d37212cc0 | ||
| 
						 | 
					9ca43f0bb4 | ||
| 
						 | 
					fe8d262175 | ||
| 
						 | 
					493d6e7165 | ||
| 
						 | 
					17b630bcd8 | ||
| 
						 | 
					018c5ad3b4 | ||
| 
						 | 
					a14b4da977 | ||
| 
						 | 
					01b4597cc7 | ||
| 
						 | 
					0b083ccc1c | ||
| 
						 | 
					ca22cb0eae | ||
| 
						 | 
					bcbb8e5f8c | ||
| 
						 | 
					bea286cdd4 | ||
| 
						 | 
					892d2ee8d2 | ||
| 
						 | 
					7440608c14 | ||
| 
						 | 
					6246f0295b | ||
| 
						 | 
					53c39d9b43 | ||
| 
						 | 
					9a50b60031 | ||
| 
						 | 
					fd74527320 | ||
| 
						 | 
					f91fb522ac | ||
| 
						 | 
					7019c02b18 | ||
| 
						 | 
					94f8422b9b | ||
| 
						 | 
					b25c01567a | ||
| 
						 | 
					b9329885de | ||
| 
						 | 
					ac88c4cff9 | ||
| 
						 | 
					9674f2d238 | ||
| 
						 | 
					38c9ecb6f1 | ||
| 
						 | 
					be9e9cdd5a | ||
| 
						 | 
					afdaf07446 | ||
| 
						 | 
					5caaf10225 | ||
| 
						 | 
					daffc3a776 | ||
| 
						 | 
					34c9748ce5 | ||
| 
						 | 
					f17acbf4d1 | ||
| 
						 | 
					229f56ce2f | ||
| 
						 | 
					0847bedc51 | ||
| 
						 | 
					9e4546c305 | ||
| 
						 | 
					97b01bf26a | ||
| 
						 | 
					e7011628d0 | ||
| 
						 | 
					044ffdecbd | ||
| 
						 | 
					1a06a3543b | ||
| 
						 | 
					5fedc0bf1e | ||
| 
						 | 
					7a881f867e | ||
| 
						 | 
					dfe73a6cfb | ||
| 
						 | 
					45d0fb27ad | ||
| 
						 | 
					31890af61d | ||
| 
						 | 
					a54b3ecc15 | ||
| 
						 | 
					c37cd1bd51 | ||
| 
						 | 
					0e3ee89cbf | ||
| 
						 | 
					a049176c14 | ||
| 
						 | 
					5ef2b0089e | ||
| 
						 | 
					1cc4899593 | ||
| 
						 | 
					26d1390be9 | ||
| 
						 | 
					dc65584fb3 | ||
| 
						 | 
					b3d5a708d5 | ||
| 
						 | 
					904a5d296b | ||
| 
						 | 
					cc82e6a47a | ||
| 
						 | 
					a9d02ec854 | ||
| 
						 | 
					6b5a2d3767 | ||
| 
						 | 
					10d9060b94 | ||
| 
						 | 
					8441839c01 | ||
| 
						 | 
					b669f5838f | ||
| 
						 | 
					1a942f9ce1 | ||
| 
						 | 
					ae840758f7 | ||
| 
						 | 
					082c08e5f9 | ||
| 
						 | 
					74a43557ab | ||
| 
						 | 
					184271789d | ||
| 
						 | 
					fa61b0cad8 | ||
| 
						 | 
					7b197a90d1 | ||
| 
						 | 
					62384af2f6 | ||
| 
						 | 
					b466202a1a | ||
| 
						 | 
					7ab38c18d8 | ||
| 
						 | 
					39841ee43e | ||
| 
						 | 
					e419800d98 | ||
| 
						 | 
					63c99ab69a | ||
| 
						 | 
					e2a8fd2279 | ||
| 
						 | 
					bbde520471 | ||
| 
						 | 
					45fb12f98e | ||
| 
						 | 
					4be3dcce50 | ||
| 
						 | 
					d12a41c769 | ||
| 
						 | 
					ed9a58c9ed | ||
| 
						 | 
					4389cea5a1 | ||
| 
						 | 
					d24854920b | ||
| 
						 | 
					9902443bee | ||
| 
						 | 
					8040b2ef16 | ||
| 
						 | 
					4533680b10 | ||
| 
						 | 
					3b28175135 | ||
| 
						 | 
					8e22812265 | ||
| 
						 | 
					409bc91b9e | ||
| 
						 | 
					61fe543a2a | ||
| 
						 | 
					8a949a7e64 | ||
| 
						 | 
					b0dc9fb97a | ||
| 
						 | 
					83114d1002 | ||
| 
						 | 
					94a25a903f | ||
| 
						 | 
					4f402f9e55 | ||
| 
						 | 
					27bb2e3dcc | ||
| 
						 | 
					66cc52f6ec | ||
| 
						 | 
					ed1367b116 | ||
| 
						 | 
					5c055352e4 | ||
| 
						 | 
					8dcff5ada1 | ||
| 
						 | 
					4b8b23d7d5 | ||
| 
						 | 
					e576f6aed3 | ||
| 
						 | 
					97ab04da91 | ||
| 
						 | 
					c6361bb36e | ||
| 
						 | 
					b0b3dc225d | ||
| 
						 | 
					9cbaba192a | ||
| 
						 | 
					f511598e13 | ||
| 
						 | 
					5ffff3b7aa | ||
| 
						 | 
					24c4fa1c4f | ||
| 
						 | 
					7f312c1273 | ||
| 
						 | 
					ef5c226c81 | ||
| 
						 | 
					3f029cf799 | ||
| 
						 | 
					5bf64a8bec | ||
| 
						 | 
					89f9c537f8 | ||
| 
						 | 
					e8d7f57818 | ||
| 
						 | 
					9b42fa78e7 | ||
| 
						 | 
					96d98e8f39 | ||
| 
						 | 
					1f3a281e78 | ||
| 
						 | 
					dab4b66ee0 | ||
| 
						 | 
					64dd192ddb | ||
| 
						 | 
					b0a5c383d0 | ||
| 
						 | 
					4836cc99eb | ||
| 
						 | 
					ba33873354 | ||
| 
						 | 
					12c838cab0 | ||
| 
						 | 
					a83a1c83a7 | ||
| 
						 | 
					2234f47170 | ||
| 
						 | 
					4bae1b17fc | ||
| 
						 | 
					6a7a0e0fa6 | ||
| 
						 | 
					bff987c55f | ||
| 
						 | 
					e467006913 | ||
| 
						 | 
					1d646d4a6d | ||
| 
						 | 
					70bd4936a1 | ||
| 
						 | 
					5ae7add6d2 | ||
| 
						 | 
					02b206ce7c | ||
| 
						 | 
					2ea626a567 | ||
| 
						 | 
					653909ec11 | ||
| 
						 | 
					1d7c148eaf | ||
| 
						 | 
					94a7e130ca | ||
| 
						 | 
					609abacdf0 | ||
| 
						 | 
					033ff2e49a | ||
| 
						 | 
					00a9bf0641 | ||
| 
						 | 
					100f322456 | ||
| 
						 | 
					2251e08d30 | ||
| 
						 | 
					14e9beef53 | ||
| 
						 | 
					c7cce23d54 | ||
| 
						 | 
					7becf32352 | ||
| 
						 | 
					741eda9f4e | ||
| 
						 | 
					473f51481c | ||
| 
						 | 
					684f9bd11b | ||
| 
						 | 
					b4b7f0c360 | ||
| 
						 | 
					cf42d02628 | ||
| 
						 | 
					cc9a9eab7b | ||
| 
						 | 
					784d6abc9b | ||
| 
						 | 
					0eafc07184 | ||
| 
						 | 
					7e9499bd2e | ||
| 
						 | 
					d01a52aa95 | ||
| 
						 | 
					6bcd492980 | ||
| 
						 | 
					96b15da04b | ||
| 
						 | 
					e0f1801693 | ||
| 
						 | 
					d195ad85c5 | ||
| 
						 | 
					773f7b6c1b | ||
| 
						 | 
					40f29fe399 | ||
| 
						 | 
					b19eb117c1 | ||
| 
						 | 
					f930ba5eff | ||
| 
						 | 
					fbbda6c230 | ||
| 
						 | 
					47db4968e5 | ||
| 
						 | 
					1aff2e518c | ||
| 
						 | 
					24134ca49e | ||
| 
						 | 
					aadbe05a5e | ||
| 
						 | 
					18eddf4700 | ||
| 
						 | 
					0869ba7e70 | ||
| 
						 | 
					cba5db1c53 | ||
| 
						 | 
					0aa5430e23 | ||
| 
						 | 
					88a91828cb | ||
| 
						 | 
					27af89f5fe | ||
| 
						 | 
					6f6d483bfe | ||
| 
						 | 
					915072c191 | ||
| 
						 | 
					76077b92ae | ||
| 
						 | 
					f0d1a527a5 | ||
| 
						 | 
					fdb2a96d1c | ||
| 
						 | 
					849e3ac187 | ||
| 
						 | 
					5c4cf21c59 | ||
| 
						 | 
					40ee3e5d9b | ||
| 
						 | 
					77a7cc034e | ||
| 
						 | 
					82b7881765 | ||
| 
						 | 
					3515775267 | ||
| 
						 | 
					10e12aa1c1 | ||
| 
						 | 
					7fb4e62aa3 | ||
| 
						 | 
					2605eaa614 | ||
| 
						 | 
					8e97b4820d | ||
| 
						 | 
					89af4cdb68 | ||
| 
						 | 
					6099c1a25a | ||
| 
						 | 
					fd23758aea | ||
| 
						 | 
					8ad0b89db1 | ||
| 
						 | 
					e7dffa3ff4 | ||
| 
						 | 
					2772b5d2e2 | ||
| 
						 | 
					df6d9b5f70 | ||
| 
						 | 
					c9622d6d57 | ||
| 
						 | 
					1f0b7c3c7e | ||
| 
						 | 
					c12245037e | ||
| 
						 | 
					fe6d2a50a5 | ||
| 
						 | 
					7be84950f8 | ||
| 
						 | 
					ba212da222 | ||
| 
						 | 
					122b833256 | ||
| 
						 | 
					7c73479041 | ||
| 
						 | 
					151f2e99ae | ||
| 
						 | 
					da367e813f | ||
| 
						 | 
					9143dfe336 | ||
| 
						 | 
					29914d6a72 | ||
| 
						 | 
					0dbef72a97 | ||
| 
						 | 
					3bb118bfe1 | ||
| 
						 | 
					85ce3be077 | ||
| 
						 | 
					064c2bf0a8 | ||
| 
						 | 
					c96d06ccc5 | ||
| 
						 | 
					a658dbb753 | ||
| 
						 | 
					bf5d5b629e | ||
| 
						 | 
					241d576519 | ||
| 
						 | 
					7807e9bdd0 | ||
| 
						 | 
					fcbf15fed6 | ||
| 
						 | 
					cd90a11209 | ||
| 
						 | 
					ff65707ea2 | ||
| 
						 | 
					b16026070c | ||
| 
						 | 
					1b5fbf86d8 | ||
| 
						 | 
					5eb1be8101 | ||
| 
						 | 
					6a863cd26a | ||
| 
						 | 
					87ebb7b6c7 | ||
| 
						 | 
					1233a74307 | ||
| 
						 | 
					221890acfb | ||
| 
						 | 
					687c7e766e | ||
| 
						 | 
					ba22c407d3 | ||
| 
						 | 
					a8b9ed56b5 | ||
| 
						 | 
					3004869e19 | ||
| 
						 | 
					1145853fe1 | ||
| 
						 | 
					f1617a25b1 | ||
| 
						 | 
					932be558d9 | ||
| 
						 | 
					ee3a37d3b9 | ||
| 
						 | 
					5ac412a582 | ||
| 
						 | 
					1625290bc3 | ||
| 
						 | 
					1ec169ad49 | ||
| 
						 | 
					53be552e44 | ||
| 
						 | 
					d3a75d46b9 | ||
| 
						 | 
					256512c961 | ||
| 
						 | 
					5c12ac5bcc | ||
| 
						 | 
					02a2bcb113 | ||
| 
						 | 
					1f0a01c725 | ||
| 
						 | 
					6ea164ede1 | ||
| 
						 | 
					65bae85ecc | ||
| 
						 | 
					2fd7dcf4d0 | ||
| 
						 | 
					aa3bbbe038 | ||
| 
						 | 
					6cb09a6f95 | ||
| 
						 | 
					45868f05d3 | ||
| 
						 | 
					aac7401e20 | ||
| 
						 | 
					c2fc290edd | ||
| 
						 | 
					330357fc36 | ||
| 
						 | 
					f4a3d6a64e | ||
| 
						 | 
					2e0963ec81 | ||
| 
						 | 
					897cb6e62a | ||
| 
						 | 
					80868bd48e | ||
| 
						 | 
					b3df78c56f | ||
| 
						 | 
					783a4259e3 | ||
| 
						 | 
					fcf19b8dc8 | ||
| 
						 | 
					1f9f89817d | ||
| 
						 | 
					7b94da7d85 | ||
| 
						 | 
					164406f6c2 | ||
| 
						 | 
					90ef2adc6b | ||
| 
						 | 
					d65f10f88b | ||
| 
						 | 
					dca0ece9e0 | ||
| 
						 | 
					baabc155c8 | ||
| 
						 | 
					7eb94412d6 | ||
| 
						 | 
					0fc8b24f85 | ||
| 
						 | 
					88f6ef5b96 | ||
| 
						 | 
					7442483419 | ||
| 
						 | 
					9c61933c04 | ||
| 
						 | 
					2b36a99720 | ||
| 
						 | 
					c2cfc42ba4 | ||
| 
						 | 
					4f32704e08 | ||
| 
						 | 
					fccf43685f | ||
| 
						 | 
					39135d81ad | ||
| 
						 | 
					ff4b10681e | ||
| 
						 | 
					10b7908fc2 | ||
| 
						 | 
					31790da8c6 | ||
| 
						 | 
					4621201c47 | ||
| 
						 | 
					692ac31dbf | ||
| 
						 | 
					ed1d163d55 | ||
| 
						 | 
					e931c9040c | ||
| 
						 | 
					f23cb48ea4 | ||
| 
						 | 
					c3b2b6b07b | ||
| 
						 | 
					f89bf590ba | ||
| 
						 | 
					fea17dc00b | ||
| 
						 | 
					dbb1069920 | ||
| 
						 | 
					afc4ccfaa9 | ||
| 
						 | 
					38d55a1c07 | ||
| 
						 | 
					5fca29f103 | ||
| 
						 | 
					78f34b2ca4 | ||
| 
						 | 
					8e8028e809 | ||
| 
						 | 
					8dc70687f8 | ||
| 
						 | 
					fa22f9ee64 | ||
| 
						 | 
					b33c0d3f81 | ||
| 
						 | 
					339009add4 | ||
| 
						 | 
					798c823c4b | ||
| 
						 | 
					14ad86f2a3 | ||
| 
						 | 
					a3a714dc17 | ||
| 
						 | 
					e8d8b0d41d | ||
| 
						 | 
					17daad8f89 | ||
| 
						 | 
					e178263b3b | ||
| 
						 | 
					4febbc261e | ||
| 
						 | 
					ef3a6942fd | ||
| 
						 | 
					f8ac2be62b | ||
| 
						 | 
					25447c34e5 | ||
| 
						 | 
					37cdf8fd48 | ||
| 
						 | 
					ac8e7dc959 | ||
| 
						 | 
					8293382840 | ||
| 
						 | 
					d85a3e7743 | ||
| 
						 | 
					f3f5ffb5c8 | ||
| 
						 | 
					99e6702c62 | ||
| 
						 | 
					e143c25078 | ||
| 
						 | 
					3fb67972be | ||
| 
						 | 
					61c04d4e09 | ||
| 
						 | 
					b22f728958 | ||
| 
						 | 
					112be7e383 | ||
| 
						 | 
					6a7f83fed5 | ||
| 
						 | 
					4b9698a735 | ||
| 
						 | 
					ec3d203a6d | ||
| 
						 | 
					e77b8f5475 | ||
| 
						 | 
					da8bbd321f | ||
| 
						 | 
					98fd011def | ||
| 
						 | 
					9dccbc5316 | ||
| 
						 | 
					03fa26daf9 | ||
| 
						 | 
					fbc41e3895 | ||
| 
						 | 
					f1a6d0c02c | ||
| 
						 | 
					67f6bb7155 | ||
| 
						 | 
					86282f596c | ||
| 
						 | 
					e0f24c795c | ||
| 
						 | 
					7d44ed860c | ||
| 
						 | 
					091094a24c | ||
| 
						 | 
					ba18ee518c | ||
| 
						 | 
					513a031691 | ||
| 
						 | 
					cc79de1106 | ||
| 
						 | 
					bd90e8efb2 | ||
| 
						 | 
					4bd88ff11d | ||
| 
						 | 
					d27ee61292 | ||
| 
						 | 
					0c20b3345f | ||
| 
						 | 
					0f3c8c7193 | ||
| 
						 | 
					16761ec605 | ||
| 
						 | 
					1069440cda | ||
| 
						 | 
					60f5702f17 | ||
| 
						 | 
					ee70133e47 | ||
| 
						 | 
					06a9fdeb2e | ||
| 
						 | 
					f14bd62004 | ||
| 
						 | 
					67cbaf22b7 | ||
| 
						 | 
					60b166dba2 | ||
| 
						 | 
					66e1647ede | ||
| 
						 | 
					a1a35c00a5 | ||
| 
						 | 
					b02e3361c4 | ||
| 
						 | 
					2b9ceaa25a | ||
| 
						 | 
					109b5a9755 | ||
| 
						 | 
					cc4df86c10 | ||
| 
						 | 
					e93532c395 | ||
| 
						 | 
					6ff688326a | ||
| 
						 | 
					eda5d2872f | ||
| 
						 | 
					dc88394f5f | ||
| 
						 | 
					ec85c9a2c6 | ||
| 
						 | 
					1341638556 | ||
| 
						 | 
					4875dfee11 | ||
| 
						 | 
					d834ba8bd4 | ||
| 
						 | 
					6191067771 | ||
| 
						 | 
					6c3a571163 | ||
| 
						 | 
					5efb07e10e | ||
| 
						 | 
					b25ec18ce6 | ||
| 
						 | 
					f3cd281241 | ||
| 
						 | 
					58b93cbf4c | ||
| 
						 | 
					57fc0349ff | ||
| 
						 | 
					d0a2cea772 | ||
| 
						 | 
					58d5801fb5 | ||
| 
						 | 
					e5f2e59798 | ||
| 
						 | 
					1166921057 | ||
| 
						 | 
					aeb49576f2 | ||
| 
						 | 
					7ef1fecef8 | ||
| 
						 | 
					fd630373b5 | ||
| 
						 | 
					d365d8f170 | ||
| 
						 | 
					dfa5e1172f | ||
| 
						 | 
					daf195898a | ||
| 
						 | 
					c61c0a39cf | ||
| 
						 | 
					6137e6baa5 | ||
| 
						 | 
					fd16fd9ffe | ||
| 
						 | 
					2b8bd5f2cc | ||
| 
						 | 
					d312c2e9e7 | ||
| 
						 | 
					578b0d2268 | ||
| 
						 | 
					ffb41b0109 | ||
| 
						 | 
					c63fb5d796 | ||
| 
						 | 
					8caee732e8 | ||
| 
						 | 
					df5381adce | ||
| 
						 | 
					f34836b7fa | ||
| 
						 | 
					44b459883a | ||
| 
						 | 
					14a8592ae3 | ||
| 
						 | 
					787d0dce4a | ||
| 
						 | 
					8be05ff93d | ||
| 
						 | 
					3014af565c | ||
| 
						 | 
					315252bdc4 | ||
| 
						 | 
					c36c3b4607 | ||
| 
						 | 
					7d5e939bab | ||
| 
						 | 
					f75c9d1eed | ||
| 
						 | 
					38cb9855ea | ||
| 
						 | 
					ed8b56d624 | ||
| 
						 | 
					bca48b13ae | ||
| 
						 | 
					29426edb05 | ||
| 
						 | 
					33a2dc687f | ||
| 
						 | 
					e2398a21b2 | ||
| 
						 | 
					f19530276e | ||
| 
						 | 
					2b9b92a78c | ||
| 
						 | 
					99c55dac10 | ||
| 
						 | 
					25667e46f9 | ||
| 
						 | 
					2f4e8f2399 | ||
| 
						 | 
					9169183769 | ||
| 
						 | 
					c420f50831 | ||
| 
						 | 
					c6c9279ef4 | ||
| 
						 | 
					c125e2991d | ||
| 
						 | 
					5348b19d6a | ||
| 
						 | 
					afd426daac | ||
| 
						 | 
					202c511cfa | ||
| 
						 | 
					651eb295a4 | ||
| 
						 | 
					d798aaed33 | ||
| 
						 | 
					63ef347cc9 | ||
| 
						 | 
					3139b2d5a0 | ||
| 
						 | 
					2c80bbb244 | ||
| 
						 | 
					af06755ada | ||
| 
						 | 
					97d6dbaa6c | ||
| 
						 | 
					74cf82a1c7 | ||
| 
						 | 
					74634889ab | ||
| 
						 | 
					dbe30fcd77 | ||
| 
						 | 
					24a7f3a320 | ||
| 
						 | 
					d3650f1145 | ||
| 
						 | 
					4801db9050 | ||
| 
						 | 
					6445281658 | ||
| 
						 | 
					87719f5938 | ||
| 
						 | 
					5ba1ec433b | ||
| 
						 | 
					7c5b382458 | ||
| 
						 | 
					8960426128 | ||
| 
						 | 
					e1d47d5a92 | ||
| 
						 | 
					9407c272aa | ||
| 
						 | 
					bccbd7b400 | ||
| 
						 | 
					a24dc010ec | ||
| 
						 | 
					c5e5d50fb8 | ||
| 
						 | 
					be1c520320 | ||
| 
						 | 
					3d18d0f893 | ||
| 
						 | 
					c3351a38a6 | ||
| 
						 | 
					0b86340a8d | ||
| 
						 | 
					829371b032 | ||
| 
						 | 
					b342207bc7 | ||
| 
						 | 
					58f4fdced3 | ||
| 
						 | 
					253844cbcf | ||
| 
						 | 
					e2e2b9ffb4 | ||
| 
						 | 
					2568042f5f | ||
| 
						 | 
					722edf4b9a | ||
| 
						 | 
					e406d364e4 | ||
| 
						 | 
					31c37f41b2 | ||
| 
						 | 
					6cd879bbc5 | ||
| 
						 | 
					b7974050fe | ||
| 
						 | 
					e9c9d0816e | ||
| 
						 | 
					a437692e1a | ||
| 
						 | 
					2a14d2f3c8 | ||
| 
						 | 
					559e2d1889 | ||
| 
						 | 
					a1aa919f80 | ||
| 
						 | 
					cbccb27a5a | ||
| 
						 | 
					bbad36d576 | ||
| 
						 | 
					df6c9d55b5 | ||
| 
						 | 
					36a1d9c364 | ||
| 
						 | 
					1f3681d5ac | ||
| 
						 | 
					0159f8e53f | ||
| 
						 | 
					0432be64fc | ||
| 
						 | 
					481e062961 | ||
| 
						 | 
					72b8abdeb6 | ||
| 
						 | 
					7ec3ee41d1 | ||
| 
						 | 
					b765fa4769 | ||
| 
						 | 
					06685b162e | ||
| 
						 | 
					7bcad7c424 | ||
| 
						 | 
					6e054b3cc6 | ||
| 
						 | 
					e643f6b0f8 | ||
| 
						 | 
					e44c0f85c2 | ||
| 
						 | 
					ea66e968eb | ||
| 
						 | 
					f7e73d804e | ||
| 
						 | 
					2ff4df56a1 | ||
| 
						 | 
					346c45fe0c | ||
| 
						 | 
					f98b42a36e | ||
| 
						 | 
					f77a0a266c | ||
| 
						 | 
					0be672788f | ||
| 
						 | 
					e2e3d11d42 | ||
| 
						 | 
					bb127bb567 | ||
| 
						 | 
					8eb4f89db8 | ||
| 
						 | 
					4a87ea3e70 | ||
| 
						 | 
					61115fce99 | ||
| 
						 | 
					4f31120394 | ||
| 
						 | 
					b085cd65ce | ||
| 
						 | 
					f76ec0721a | ||
| 
						 | 
					ef3944fbbf | ||
| 
						 | 
					b158fbc0d8 | ||
| 
						 | 
					68f2d66e97 | ||
| 
						 | 
					0cdd1735bd | ||
| 
						 | 
					70f5ead20b | ||
| 
						 | 
					574e5616f8 | ||
| 
						 | 
					94fa810590 | ||
| 
						 | 
					37f69da701 | ||
| 
						 | 
					f2f8448ade | ||
| 
						 | 
					b357d3fff2 | ||
| 
						 | 
					b417194905 | ||
| 
						 | 
					d8741da20a | ||
| 
						 | 
					c469be9a62 | ||
| 
						 | 
					ff4eb339ef | ||
| 
						 | 
					78489383c0 | ||
| 
						 | 
					f67c1b415f | ||
| 
						 | 
					570c16d921 | ||
| 
						 | 
					c8bc60568a | ||
| 
						 | 
					73de3ba856 | ||
| 
						 | 
					6a69be8537 | ||
| 
						 | 
					3ee381d505 | ||
| 
						 | 
					7ce744e2e4 | ||
| 
						 | 
					b81d21c991 | ||
| 
						 | 
					e9fe0992c6 | ||
| 
						 | 
					d5ef9018fa | ||
| 
						 | 
					c2737a7c51 | ||
| 
						 | 
					4b6d8733c6 | ||
| 
						 | 
					f22a3e0955 | ||
| 
						 | 
					4b8144a2f7 | ||
| 
						 | 
					abac52e23c | ||
| 
						 | 
					9aa089313e | ||
| 
						 | 
					b18f2c481a | ||
| 
						 | 
					ec83b9f77b | ||
| 
						 | 
					fad5495e02 | ||
| 
						 | 
					70dec1171e | ||
| 
						 | 
					0673a6fce3 | ||
| 
						 | 
					8f525b1407 | ||
| 
						 | 
					e83a991a4b | ||
| 
						 | 
					c000432a52 | ||
| 
						 | 
					c06d2d6927 | ||
| 
						 | 
					aa29653a8f | ||
| 
						 | 
					3f7e4ad486 | ||
| 
						 | 
					0eab546b2f | ||
| 
						 | 
					8830d216d1 | ||
| 
						 | 
					9fa1e3d449 | ||
| 
						 | 
					fe5dce7159 | ||
| 
						 | 
					952fa31548 | ||
| 
						 | 
					cc058ccc61 | ||
| 
						 | 
					bc26ed9701 | ||
| 
						 | 
					26135fc1a0 | ||
| 
						 | 
					0bd14672ff | ||
| 
						 | 
					7c7150cde8 | ||
| 
						 | 
					b08282b0c1 | ||
| 
						 | 
					3bf6e1befc | ||
| 
						 | 
					52692371ac | ||
| 
						 | 
					159ac1d8bb | ||
| 
						 | 
					7f1ffdbc79 | ||
| 
						 | 
					5fe64931dc | ||
| 
						 | 
					ee67855b48 | ||
| 
						 | 
					594a97f43f | ||
| 
						 | 
					7fb435a8b4 | ||
| 
						 | 
					01e74d6116 | ||
| 
						 | 
					b321d75b39 | ||
| 
						 | 
					1e47a45723 | ||
| 
						 | 
					88c609e5ef | ||
| 
						 | 
					775d1a424e | 
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -362,5 +362,8 @@ MigrationBackup/
 | 
			
		||||
# Fody - auto-generated XML schema
 | 
			
		||||
FodyWeavers.xsd
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/framework/*Pro*
 | 
			
		||||
/src/*Pro*/
 | 
			
		||||
/src/*Pro*
 | 
			
		||||
/src/*pro*
 | 
			
		||||
/src/*pro*/
 | 
			
		||||
/src/ThingsGateway.Server/Configuration/GiteeOAuthSettings.json
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								LICENSE
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Cachetribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
							
								
								
									
										202
									
								
								LICENSE.txt
									
									
									
									
									
								
							
							
						
						
									
										202
									
								
								LICENSE.txt
									
									
									
									
									
								
							@@ -1,202 +0,0 @@
 | 
			
		||||
 | 
			
		||||
                                 Apache License
 | 
			
		||||
                           Version 2.0, January 2004
 | 
			
		||||
                        http://www.apache.org/licenses/
 | 
			
		||||
 | 
			
		||||
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 | 
			
		||||
 | 
			
		||||
   1. Definitions.
 | 
			
		||||
 | 
			
		||||
      "License" shall mean the terms and conditions for use, reproduction,
 | 
			
		||||
      and distribution as defined by Sections 1 through 9 of this document.
 | 
			
		||||
 | 
			
		||||
      "Licensor" shall mean the copyright owner or entity authorized by
 | 
			
		||||
      the copyright owner that is granting the License.
 | 
			
		||||
 | 
			
		||||
      "Legal Entity" shall mean the union of the acting entity and all
 | 
			
		||||
      other entities that control, are controlled by, or are under common
 | 
			
		||||
      control with that entity. For the purposes of this definition,
 | 
			
		||||
      "control" means (i) the power, direct or indirect, to cause the
 | 
			
		||||
      direction or management of such entity, whether by contract or
 | 
			
		||||
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
 | 
			
		||||
      outstanding shares, or (iii) beneficial ownership of such entity.
 | 
			
		||||
 | 
			
		||||
      "You" (or "Your") shall mean an individual or Legal Entity
 | 
			
		||||
      exercising permissions granted by this License.
 | 
			
		||||
 | 
			
		||||
      "Source" form shall mean the preferred form for making modifications,
 | 
			
		||||
      including but not limited to software source code, documentation
 | 
			
		||||
      source, and configuration files.
 | 
			
		||||
 | 
			
		||||
      "Object" form shall mean any form resulting from mechanical
 | 
			
		||||
      transformation or translation of a Source form, including but
 | 
			
		||||
      not limited to compiled object code, generated documentation,
 | 
			
		||||
      and conversions to other media types.
 | 
			
		||||
 | 
			
		||||
      "Work" shall mean the work of authorship, whether in Source or
 | 
			
		||||
      Object form, made available under the License, as indicated by a
 | 
			
		||||
      copyright notice that is included in or attached to the work
 | 
			
		||||
      (an example is provided in the Appendix below).
 | 
			
		||||
 | 
			
		||||
      "Derivative Works" shall mean any work, whether in Source or Object
 | 
			
		||||
      form, that is based on (or derived from) the Work and for which the
 | 
			
		||||
      editorial revisions, annotations, elaborations, or other modifications
 | 
			
		||||
      represent, as a whole, an original work of authorship. For the purposes
 | 
			
		||||
      of this License, Derivative Works shall not include works that remain
 | 
			
		||||
      separable from, or merely link (or bind by name) to the interfaces of,
 | 
			
		||||
      the Work and Derivative Works thereof.
 | 
			
		||||
 | 
			
		||||
      "Contribution" shall mean any work of authorship, including
 | 
			
		||||
      the original version of the Work and any modifications or additions
 | 
			
		||||
      to that Work or Derivative Works thereof, that is intentionally
 | 
			
		||||
      submitted to Licensor for inclusion in the Work by the copyright owner
 | 
			
		||||
      or by an individual or Legal Entity authorized to submit on behalf of
 | 
			
		||||
      the copyright owner. For the purposes of this definition, "submitted"
 | 
			
		||||
      means any form of electronic, verbal, or written communication sent
 | 
			
		||||
      to the Licensor or its representatives, including but not limited to
 | 
			
		||||
      communication on electronic mailing lists, source code control systems,
 | 
			
		||||
      and issue tracking systems that are managed by, or on behalf of, the
 | 
			
		||||
      Licensor for the purpose of discussing and improving the Work, but
 | 
			
		||||
      excluding communication that is conspicuously marked or otherwise
 | 
			
		||||
      designated in writing by the copyright owner as "Not a Contribution."
 | 
			
		||||
 | 
			
		||||
      "Contributor" shall mean Licensor and any individual or Legal Entity
 | 
			
		||||
      on behalf of whom a Contribution has been received by Licensor and
 | 
			
		||||
      subsequently incorporated within the Work.
 | 
			
		||||
 | 
			
		||||
   2. Grant of Copyright License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      copyright license to reproduce, prepare Derivative Works of,
 | 
			
		||||
      publicly display, publicly perform, sublicense, and distribute the
 | 
			
		||||
      Work and such Derivative Works in Source or Object form.
 | 
			
		||||
 | 
			
		||||
   3. Grant of Patent License. Subject to the terms and conditions of
 | 
			
		||||
      this License, each Contributor hereby grants to You a perpetual,
 | 
			
		||||
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 | 
			
		||||
      (except as stated in this section) patent license to make, have made,
 | 
			
		||||
      use, offer to sell, sell, import, and otherwise transfer the Work,
 | 
			
		||||
      where such license applies only to those patent claims licensable
 | 
			
		||||
      by such Contributor that are necessarily infringed by their
 | 
			
		||||
      Contribution(s) alone or by combination of their Contribution(s)
 | 
			
		||||
      with the Work to which such Contribution(s) was submitted. If You
 | 
			
		||||
      institute patent litigation against any entity (including a
 | 
			
		||||
      cross-claim or counterclaim in a lawsuit) alleging that the Work
 | 
			
		||||
      or a Contribution incorporated within the Work constitutes direct
 | 
			
		||||
      or contributory patent infringement, then any patent licenses
 | 
			
		||||
      granted to You under this License for that Work shall terminate
 | 
			
		||||
      as of the date such litigation is filed.
 | 
			
		||||
 | 
			
		||||
   4. Redistribution. You may reproduce and distribute copies of the
 | 
			
		||||
      Work or Derivative Works thereof in any medium, with or without
 | 
			
		||||
      modifications, and in Source or Object form, provided that You
 | 
			
		||||
      meet the following conditions:
 | 
			
		||||
 | 
			
		||||
      (a) You must give any other recipients of the Work or
 | 
			
		||||
          Derivative Works a copy of this License; and
 | 
			
		||||
 | 
			
		||||
      (b) You must cause any modified files to carry prominent notices
 | 
			
		||||
          stating that You changed the files; and
 | 
			
		||||
 | 
			
		||||
      (c) You must retain, in the Source form of any Derivative Works
 | 
			
		||||
          that You distribute, all copyright, patent, trademark, and
 | 
			
		||||
          attribution notices from the Source form of the Work,
 | 
			
		||||
          excluding those notices that do not pertain to any part of
 | 
			
		||||
          the Derivative Works; and
 | 
			
		||||
 | 
			
		||||
      (d) If the Work includes a "NOTICE" text file as part of its
 | 
			
		||||
          distribution, then any Derivative Works that You distribute must
 | 
			
		||||
          include a readable copy of the attribution notices contained
 | 
			
		||||
          within such NOTICE file, excluding those notices that do not
 | 
			
		||||
          pertain to any part of the Derivative Works, in at least one
 | 
			
		||||
          of the following places: within a NOTICE text file distributed
 | 
			
		||||
          as part of the Derivative Works; within the Source form or
 | 
			
		||||
          documentation, if provided along with the Derivative Works; or,
 | 
			
		||||
          within a display generated by the Derivative Works, if and
 | 
			
		||||
          wherever such third-party notices normally appear. The contents
 | 
			
		||||
          of the NOTICE file are for informational purposes only and
 | 
			
		||||
          do not modify the License. You may add Your own attribution
 | 
			
		||||
          notices within Derivative Works that You distribute, alongside
 | 
			
		||||
          or as an addendum to the NOTICE text from the Work, provided
 | 
			
		||||
          that such additional attribution notices cannot be construed
 | 
			
		||||
          as modifying the License.
 | 
			
		||||
 | 
			
		||||
      You may add Your own copyright statement to Your modifications and
 | 
			
		||||
      may provide additional or different license terms and conditions
 | 
			
		||||
      for use, reproduction, or distribution of Your modifications, or
 | 
			
		||||
      for any such Derivative Works as a whole, provided Your use,
 | 
			
		||||
      reproduction, and distribution of the Work otherwise complies with
 | 
			
		||||
      the conditions stated in this License.
 | 
			
		||||
 | 
			
		||||
   5. Submission of Contributions. Unless You explicitly state otherwise,
 | 
			
		||||
      any Contribution intentionally submitted for inclusion in the Work
 | 
			
		||||
      by You to the Licensor shall be under the terms and conditions of
 | 
			
		||||
      this License, without any additional terms or conditions.
 | 
			
		||||
      Notwithstanding the above, nothing herein shall supersede or modify
 | 
			
		||||
      the terms of any separate license agreement you may have executed
 | 
			
		||||
      with Licensor regarding such Contributions.
 | 
			
		||||
 | 
			
		||||
   6. Trademarks. This License does not grant permission to use the trade
 | 
			
		||||
      names, trademarks, service marks, or product names of the Licensor,
 | 
			
		||||
      except as required for reasonable and customary use in describing the
 | 
			
		||||
      origin of the Work and reproducing the content of the NOTICE file.
 | 
			
		||||
 | 
			
		||||
   7. Disclaimer of Warranty. Unless required by applicable law or
 | 
			
		||||
      agreed to in writing, Licensor provides the Work (and each
 | 
			
		||||
      Contributor provides its Contributions) on an "AS IS" BASIS,
 | 
			
		||||
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 | 
			
		||||
      implied, including, without limitation, any warranties or conditions
 | 
			
		||||
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
 | 
			
		||||
      PARTICULAR PURPOSE. You are solely responsible for determining the
 | 
			
		||||
      appropriateness of using or redistributing the Work and assume any
 | 
			
		||||
      risks associated with Your exercise of permissions under this License.
 | 
			
		||||
 | 
			
		||||
   8. Limitation of Liability. In no event and under no legal theory,
 | 
			
		||||
      whether in tort (including negligence), contract, or otherwise,
 | 
			
		||||
      unless required by applicable law (such as deliberate and grossly
 | 
			
		||||
      negligent acts) or agreed to in writing, shall any Contributor be
 | 
			
		||||
      liable to You for damages, including any direct, indirect, special,
 | 
			
		||||
      incidental, or consequential damages of any character arising as a
 | 
			
		||||
      result of this License or out of the use or inability to use the
 | 
			
		||||
      Work (including but not limited to damages for loss of goodwill,
 | 
			
		||||
      work stoppage, computer failure or malfunction, or any and all
 | 
			
		||||
      other commercial damages or losses), even if such Contributor
 | 
			
		||||
      has been advised of the possibility of such damages.
 | 
			
		||||
 | 
			
		||||
   9. Accepting Warranty or Additional Liability. While redistributing
 | 
			
		||||
      the Work or Derivative Works thereof, You may choose to offer,
 | 
			
		||||
      and charge a fee for, acceptance of support, warranty, indemnity,
 | 
			
		||||
      or other liability obligations and/or rights consistent with this
 | 
			
		||||
      License. However, in accepting such obligations, You may act only
 | 
			
		||||
      on Your own behalf and on Your sole responsibility, not on behalf
 | 
			
		||||
      of any other Contributor, and only if You agree to indemnify,
 | 
			
		||||
      defend, and hold each Contributor harmless for any liability
 | 
			
		||||
      incurred by, or claims asserted against, such Contributor by reason
 | 
			
		||||
      of your accepting any such warranty or additional liability.
 | 
			
		||||
 | 
			
		||||
   END OF TERMS AND CONDITIONS
 | 
			
		||||
 | 
			
		||||
   APPENDIX: How to apply the Apache License to your work.
 | 
			
		||||
 | 
			
		||||
      To apply the Apache License to your work, attach the following
 | 
			
		||||
      boilerplate notice, with the fields enclosed by brackets "[]"
 | 
			
		||||
      replaced with your own identifying information. (Don't include
 | 
			
		||||
      the brackets!)  The text should be enclosed in the appropriate
 | 
			
		||||
      comment syntax for the file format. We also recommend that a
 | 
			
		||||
      file or class name and description of purpose be included on the
 | 
			
		||||
      same "printed page" as the copyright notice for easier
 | 
			
		||||
      identification within third-party archives.
 | 
			
		||||
 | 
			
		||||
   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
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
							
								
								
									
										103
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								README.md
									
									
									
									
									
								
							@@ -1,38 +1,101 @@
 | 
			
		||||
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 **NetCore** 跨平台边缘采集网关(工业设备采集)
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 [**设备采集驱动**](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
A cross-platform, high-performance edge data collection gateway based on net9.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 **ThingsGateway** 存储库同时提供 **基于Blazor的权限框架** 查看 [**ThingsGateway.Admin**](https://gitee.com/dotnetchina/ThingsGateway/blob/master/framework/ThingsGateway.Admin.sln)
 | 
			
		||||
## Documentation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Documentation](https://thingsgateway.cn/).
 | 
			
		||||
 | 
			
		||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
## Demo
 | 
			
		||||
 | 
			
		||||
[ThingsGateway](https://diego2098.gitee.io/thingsgateway-docs/) 文档。
 | 
			
		||||
 | 
			
		||||
[Demo](https://demo.thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
Account: **SuperAdmin**
 | 
			
		||||
 | 
			
		||||
[ThingsGateway](https://gitee.com/diego2098/ThingsGateway) 采用 [Apache-2.0](https://gitee.com/diego2098/ThingsGateway/blob/master/LICENSE.zh) 开源协议。
 | 
			
		||||
 | 
			
		||||
Password: **111111**
 | 
			
		||||
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
**In the upper-right corner, switch to the IoT Gateway module in the personal popup box**
 | 
			
		||||
 | 
			
		||||
[ThingsGateway演示地址](http://120.24.62.140:5000/)
 | 
			
		||||
## Docker
 | 
			
		||||
 | 
			
		||||
账户	:  **superAdmin**	
 | 
			
		||||
```shell
 | 
			
		||||
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
 | 
			
		||||
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
[ThingsGateway赞助途径](https://diego2098.gitee.io/thingsgateway-docs/docs/donate)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Plugin List
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#### Data Collection Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| Plugin Name | Remarks                                                       |
 | 
			
		||||
| ----------- | ------------------------------------------------------------- |
 | 
			
		||||
| Modbus      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links   |
 | 
			
		||||
| SiemensS7   | Siemens PLC S7 series                                         |
 | 
			
		||||
| Dlt6452007  | Supports Serial/Tcp/Udp links                                 |
 | 
			
		||||
| OpcDaMaster | Compiled for 64-bit                                           |
 | 
			
		||||
| OpcUaMaster | Supports certificate login, object extension, Json read/write |
 | 
			
		||||
| Webhook          | Webhook                                             |
 | 
			
		||||
 | 
			
		||||
#### Business Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| Plugin Name      | Remarks                                                                                           |
 | 
			
		||||
| ---------------- | ------------------------------------------------------------------------------------------------- |
 | 
			
		||||
| ModbusSlave      | Supports Rtu/Tcp message formats, with Serial/Tcp/Udp links, supports Rpc reverse writing         |
 | 
			
		||||
| OpcUaServer      | OpcUa server, supports Rpc reverse writing                                                        |
 | 
			
		||||
| MqttClient       | Mqtt client, supports Rpc reverse writing, script-customizable upload content                     |
 | 
			
		||||
| MqttServer       | Mqtt server, supports WebSocket, supports Rpc reverse writing, script-customizable upload content |
 | 
			
		||||
| KafkaProducer    | Script-customizable upload content                                                                |
 | 
			
		||||
| RabbitMQProducer | Script-customizable upload content                                                                |
 | 
			
		||||
| SqlDB            | Relational database storage, supports historical storage and real-time data updates               |
 | 
			
		||||
| SqlHistoryAlarm      | Alarm historical data relational database storage                                                 |
 | 
			
		||||
| TDengineDB       | Time-series database storage                                                                      |
 | 
			
		||||
| QuestDB          | Time-series database storage                                                                      |
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[License](https://thingsgateway.cn/docs/1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Sponsorship
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Sponsorship Approach](https://thingsgateway.cn/docs/1000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Community
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Pro Plugins
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[Plugin List](https://thingsgateway.cn/docs/1001)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										86
									
								
								README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								README.zh-CN.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
# ThingsGateway
 | 
			
		||||
 | 
			
		||||
## 介绍
 | 
			
		||||
 | 
			
		||||
基于net9的跨平台高性能边缘采集网关
 | 
			
		||||
 | 
			
		||||
## 文档
 | 
			
		||||
 | 
			
		||||
[文档](https://thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 演示
 | 
			
		||||
 | 
			
		||||
[ThingsGateway演示地址](https://demo.thingsgateway.cn/)
 | 
			
		||||
 | 
			
		||||
账户	:  **SuperAdmin**
 | 
			
		||||
 | 
			
		||||
密码 : **111111**
 | 
			
		||||
 | 
			
		||||
**右上角个人弹出框中,切换到物联网关模块**
 | 
			
		||||
 | 
			
		||||
## Docker
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway
 | 
			
		||||
 | 
			
		||||
docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### 插件列表
 | 
			
		||||
 | 
			
		||||
#### 采集插件
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 插件名称    | 备注                                  |
 | 
			
		||||
| ----------- | ------------------------------------- |
 | 
			
		||||
| Modbus      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路 |
 | 
			
		||||
| SiemensS7   | 西门子PLC S7系列                      |
 | 
			
		||||
| Dlt6452007  | 支持串口/Tcp/Udp链路                  |
 | 
			
		||||
| OpcDaMaster | 64位编译                              |
 | 
			
		||||
| OpcUaMaster | 支持证书登录,扩展对象,Json读写      |
 | 
			
		||||
 | 
			
		||||
#### 业务插件
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 插件名称         | 备注                                                       |
 | 
			
		||||
| ---------------- | ---------------------------------------------------------- |
 | 
			
		||||
| ModbusSlave      | Rtu/Tcp报文格式,支持串口/Tcp/Udp链路,支持Rpc反写         |
 | 
			
		||||
| OpcUaServer      | OpcUa服务端,支持Rpc反写                                   |
 | 
			
		||||
| MqttClient       | Mqtt客户端,支持Rpc反写,脚本自定义上传内容                |
 | 
			
		||||
| MqttServer       | Mqtt服务端,支持WebSocket,支持Rpc反写,脚本自定义上传内容 |
 | 
			
		||||
| KafkaProducer    | 脚本自定义上传内容                                         |
 | 
			
		||||
| RabbitMQProducer | 脚本自定义上传内容                                         |
 | 
			
		||||
| SqlDB            | 关系数据库存储,支持历史存储和实时数据更新                 |
 | 
			
		||||
| SqlHistoryAlarm      | 报警历史数据关系数据库存储                                 |
 | 
			
		||||
| TDengineDB       | 时序数据库存储                                             |
 | 
			
		||||
| QuestDB          | 时序数据库存储                                             |
 | 
			
		||||
| Webhook          | Webhook                                             |
 | 
			
		||||
 | 
			
		||||
## 协议
 | 
			
		||||
 | 
			
		||||
[版权声明](https://thingsgateway.cn/docs/1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 赞助
 | 
			
		||||
 | 
			
		||||
[赞助途径](https://thingsgateway.cn/docs/1000)
 | 
			
		||||
 | 
			
		||||
## 社区
 | 
			
		||||
 | 
			
		||||
QQ群:605534569 [跳转](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)
 | 
			
		||||
 | 
			
		||||
## Pro插件
 | 
			
		||||
 | 
			
		||||
[插件列表](https://thingsgateway.cn/docs/1001)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## 特别声明
 | 
			
		||||
 | 
			
		||||
ThingsGateway 项目已加入 [dotNET China](https://gitee.com/dotnetchina)  组织。<br/>
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
[*.cs]
 | 
			
		||||
 | 
			
		||||
# CA1822: 将成员标记为 static
 | 
			
		||||
dotnet_diagnostic.CA1822.severity = none
 | 
			
		||||
 | 
			
		||||
# CA1816: Dispose 方法应调用 SuppressFinalize
 | 
			
		||||
dotnet_diagnostic.CA1816.severity = none
 | 
			
		||||
 | 
			
		||||
# CA2254: 模板应为静态表达式
 | 
			
		||||
dotnet_diagnostic.CA2254.severity = none
 | 
			
		||||
 | 
			
		||||
[*.cs]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
 | 
			
		||||
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
 | 
			
		||||
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.types.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
 | 
			
		||||
dotnet_naming_symbols.non_field_members.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_prefix = I
 | 
			
		||||
dotnet_naming_style.begins_with_i.required_suffix = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.word_separator = 
 | 
			
		||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.pascal_case.required_prefix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.required_suffix = 
 | 
			
		||||
dotnet_naming_style.pascal_case.word_separator = 
 | 
			
		||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
 | 
			
		||||
csharp_using_directive_placement = outside_namespace:silent
 | 
			
		||||
csharp_style_expression_bodied_methods = false:silent
 | 
			
		||||
csharp_style_expression_bodied_constructors = false:silent
 | 
			
		||||
csharp_style_expression_bodied_operators = false:silent
 | 
			
		||||
csharp_style_expression_bodied_properties = true:silent
 | 
			
		||||
csharp_style_expression_bodied_indexers = true:silent
 | 
			
		||||
csharp_style_expression_bodied_accessors = true:silent
 | 
			
		||||
csharp_style_expression_bodied_lambdas = true:silent
 | 
			
		||||
csharp_style_expression_bodied_local_functions = false:silent
 | 
			
		||||
csharp_style_conditional_delegate_call = true:suggestion
 | 
			
		||||
csharp_style_var_for_built_in_types = false:silent
 | 
			
		||||
csharp_style_var_when_type_is_apparent = false:silent
 | 
			
		||||
csharp_style_var_elsewhere = false:silent
 | 
			
		||||
csharp_prefer_simple_using_statement = true:suggestion
 | 
			
		||||
csharp_prefer_braces = true:silent
 | 
			
		||||
csharp_style_namespace_declarations = block_scoped:silent
 | 
			
		||||
csharp_style_prefer_method_group_conversion = true:silent
 | 
			
		||||
csharp_style_prefer_top_level_statements = true:silent
 | 
			
		||||
 | 
			
		||||
# CA2208: 正确实例化参数异常
 | 
			
		||||
dotnet_diagnostic.CA2208.severity = none
 | 
			
		||||
 | 
			
		||||
# IDE0057: 使用范围运算符
 | 
			
		||||
dotnet_diagnostic.IDE0057.severity = none
 | 
			
		||||
 | 
			
		||||
# IDE0056: 使用索引运算符
 | 
			
		||||
dotnet_diagnostic.IDE0056.severity = none
 | 
			
		||||
 | 
			
		||||
# CA1830: 最好使用 StringBuilder 的强类型 Append 和 Insert 方法重载
 | 
			
		||||
dotnet_diagnostic.CA1830.severity = none
 | 
			
		||||
 | 
			
		||||
# CA1847: 将字符型文本用于单个字符查找
 | 
			
		||||
dotnet_diagnostic.CA1847.severity = none
 | 
			
		||||
 | 
			
		||||
[*.vb]
 | 
			
		||||
#### 命名样式 ####
 | 
			
		||||
 | 
			
		||||
# 命名规则
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.symbols = interface
 | 
			
		||||
dotnet_naming_rule.interface_should_be_以_i_开始.style = 以_i_开始
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.symbols = 类型
 | 
			
		||||
dotnet_naming_rule.类型_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.severity = suggestion
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.symbols = 非字段成员
 | 
			
		||||
dotnet_naming_rule.非字段成员_should_be_帕斯卡拼写法.style = 帕斯卡拼写法
 | 
			
		||||
 | 
			
		||||
# 符号规范
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_kinds = interface
 | 
			
		||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.interface.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_kinds = class, struct, interface, enum
 | 
			
		||||
dotnet_naming_symbols.类型.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.类型.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_kinds = property, event, method
 | 
			
		||||
dotnet_naming_symbols.非字段成员.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
 | 
			
		||||
dotnet_naming_symbols.非字段成员.required_modifiers = 
 | 
			
		||||
 | 
			
		||||
# 命名样式
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_prefix = I
 | 
			
		||||
dotnet_naming_style.以_i_开始.required_suffix = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.word_separator = 
 | 
			
		||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_prefix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.required_suffix = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.word_separator = 
 | 
			
		||||
dotnet_naming_style.帕斯卡拼写法.capitalization = pascal_case
 | 
			
		||||
 | 
			
		||||
[*.{cs,vb}]
 | 
			
		||||
dotnet_style_qualification_for_field = false:silent
 | 
			
		||||
dotnet_style_qualification_for_property = false:silent
 | 
			
		||||
dotnet_style_qualification_for_method = false:silent
 | 
			
		||||
dotnet_style_qualification_for_event = false:silent
 | 
			
		||||
end_of_line = crlf
 | 
			
		||||
 | 
			
		||||
# IDE0060: 删除未使用的参数
 | 
			
		||||
dotnet_diagnostic.IDE0060.severity = none
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
<Project>
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
 | 
			
		||||
		<Version>2.1.0.6</Version>
 | 
			
		||||
		<Authors>Diego</Authors>
 | 
			
		||||
		<Product>ThingsGateway</Product>
 | 
			
		||||
		<Copyright>© 2023-present Diego</Copyright>
 | 
			
		||||
		<RepositoryUrl>https://gitee.com/diego2098/ThingsGateway</RepositoryUrl>
 | 
			
		||||
		<PublishRepositoryUrl>true</PublishRepositoryUrl>
 | 
			
		||||
		<EmbedUntrackedSource>true</EmbedUntrackedSource>
 | 
			
		||||
		<EmbedAllSources>true</EmbedAllSources>
 | 
			
		||||
		<RepositoryType>Gitee</RepositoryType>
 | 
			
		||||
		<IncludeSymbols>true</IncludeSymbols>
 | 
			
		||||
		<SymbolPackageFormat>snupkg</SymbolPackageFormat>
 | 
			
		||||
		<SignAssembly>True</SignAssembly>
 | 
			
		||||
		<DelaySign>False</DelaySign>
 | 
			
		||||
		<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<DocumentationFile>$(MSBuildProjectName).xml</DocumentationFile>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<LangVersion>latest</LangVersion>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
#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.DynamicApiController;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
#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.DynamicApiController;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 文件下载
 | 
			
		||||
/// </summary>
 | 
			
		||||
[ApiDescriptionSettings(CateGoryConst.ThingsGatewayAdmin, Order = 200)]
 | 
			
		||||
[Route("file")]
 | 
			
		||||
[LoggingMonitor]
 | 
			
		||||
public class FileController : IDynamicApiController
 | 
			
		||||
{
 | 
			
		||||
    private readonly IFileService _fileService;
 | 
			
		||||
    private readonly IOperateLogService _operateLogService;
 | 
			
		||||
    private readonly IVisitLogService _visitLogService;
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// <inheritdoc cref="FileController"/>
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public FileController(
 | 
			
		||||
        IFileService fileService,
 | 
			
		||||
        IOperateLogService operateLogService,
 | 
			
		||||
        IVisitLogService visitLogService
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _fileService = fileService;
 | 
			
		||||
        _operateLogService = operateLogService;
 | 
			
		||||
        _visitLogService = visitLogService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 下载操作日志
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    [HttpGet("operateLog")]
 | 
			
		||||
    public async Task<IActionResult> DownloadOperateLogAsync([FromQuery] OperateLogInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var memoryStream = await _operateLogService.ExportFileAsync(input);
 | 
			
		||||
        memoryStream.Seek(0, SeekOrigin.Begin);
 | 
			
		||||
        var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
 | 
			
		||||
        {
 | 
			
		||||
            FileDownloadName = $"operateLog{SysDateTimeExtensions.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);
 | 
			
		||||
        memoryStream.Seek(0, SeekOrigin.Begin);
 | 
			
		||||
        var data = new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
 | 
			
		||||
        {
 | 
			
		||||
            FileDownloadName = $"operateLog{SysDateTimeExtensions.CurrentDateTime.ToFileDateTimeFormat()}.xlsx"
 | 
			
		||||
        };
 | 
			
		||||
        return data;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,66 +0,0 @@
 | 
			
		||||
#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.DynamicApiController;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application
 | 
			
		||||
{
 | 
			
		||||
    /// <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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
#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.DynamicApiController;
 | 
			
		||||
using Furion.SpecificationDocument;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application
 | 
			
		||||
{
 | 
			
		||||
    /// <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.ToBoolean();
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
#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 System;
 | 
			
		||||
global using System.IO;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
@@ -1,15 +0,0 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
	<PropertyGroup>
 | 
			
		||||
		<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
 | 
			
		||||
	</PropertyGroup>
 | 
			
		||||
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
		<None Include="..\.editorconfig" Link=".editorconfig" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	
 | 
			
		||||
	<ItemGroup>
 | 
			
		||||
	  <ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
 | 
			
		||||
	</ItemGroup>
 | 
			
		||||
	
 | 
			
		||||
</Project>
 | 
			
		||||
@@ -1,102 +0,0 @@
 | 
			
		||||
<?xml version="1.0"?>
 | 
			
		||||
<doc>
 | 
			
		||||
    <assembly>
 | 
			
		||||
        <name>ThingsGateway.Admin.ApiController</name>
 | 
			
		||||
    </assembly>
 | 
			
		||||
    <members>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.Application.AuthController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登录控制器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.AuthController.#ctor(ThingsGateway.Admin.Application.IAuthService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.Application.AuthController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="authService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.AuthController.LoginAsync(ThingsGateway.Admin.Application.LoginInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="input"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.AuthController.LogoutAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            后台登出
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.Application.FileController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            文件下载
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.FileController.#ctor(ThingsGateway.Admin.Application.IFileService,ThingsGateway.Admin.Application.IOperateLogService,ThingsGateway.Admin.Application.IVisitLogService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.Application.FileController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.FileController.DownloadOperateLogAsync(ThingsGateway.Admin.Application.OperateLogInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            下载操作日志
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.FileController.DownloadVisitLogAsync(ThingsGateway.Admin.Application.VisitLogInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            下载访问日志
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.Application.OpenApiAuthController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpenApi登录控制器
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.#ctor(ThingsGateway.Admin.Application.IOpenApiAuthService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.Application.OpenApiAuthController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="authService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.LoginOpenApiAsync(ThingsGateway.Admin.Application.LoginOpenApiInput)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            OpenApi登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="input"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.OpenApiAuthController.LogoutAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            登出
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="T:ThingsGateway.Admin.Application.SwaggerController">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录授权服务
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.SwaggerController.#ctor(ThingsGateway.Admin.Application.ConfigService)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            <inheritdoc cref="T:ThingsGateway.Admin.Application.SwaggerController"/>
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="sysConfigService"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.SwaggerController.SwaggerCheckUrlAsync">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录检查
 | 
			
		||||
            </summary>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:ThingsGateway.Admin.Application.SwaggerController.SwaggerSubmitUrlAsync(Furion.SpecificationDocument.SpecificationAuth)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            Swagger登录
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="auth"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
    </members>
 | 
			
		||||
</doc>
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
}
 | 
			
		||||
@@ -1,225 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
using Furion.Reflection;
 | 
			
		||||
using Furion.Reflection.Extensions;
 | 
			
		||||
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
    /// <exception cref="NotImplementedException"></exception>
 | 
			
		||||
    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 static 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)
 | 
			
		||||
        {
 | 
			
		||||
            clientInfo = StaticParser.Parser.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 = SysDateTimeExtensions.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.InsertableWithAttr(log).ExecuteCommand();//入库
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#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 操作
 | 
			
		||||
}
 | 
			
		||||
@@ -1,83 +0,0 @@
 | 
			
		||||
#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";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
#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 关系表
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,92 +0,0 @@
 | 
			
		||||
#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>
 | 
			
		||||
    /// 登录验证码开关
 | 
			
		||||
    /// </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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
#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退出";
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
#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 日志表
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
#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";
 | 
			
		||||
}
 | 
			
		||||
@@ -1,44 +0,0 @@
 | 
			
		||||
#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 关系表
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
#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 System;
 | 
			
		||||
global using System.Collections.Generic;
 | 
			
		||||
global using System.IO;
 | 
			
		||||
global using System.Linq;
 | 
			
		||||
global using System.Threading;
 | 
			
		||||
global using System.Threading.Tasks;
 | 
			
		||||
@@ -1,141 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
using Yitter.IdGenerator;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 扩展方法
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PermissionUtil
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取WebApi授权树
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static List<OpenApiPermissionTreeSelector> OpenApiPermissionTreeSelector()
 | 
			
		||||
    {
 | 
			
		||||
        var cacheKey = $"{nameof(OpenApiPermissionTreeSelector)}-{CultureInfo.CurrentUICulture.Name}";
 | 
			
		||||
        List<OpenApiPermissionTreeSelector> displayName = CacheStatic.Cache.GetOrCreate(cacheKey, entry =>
 | 
			
		||||
        {
 | 
			
		||||
            List<OpenApiPermissionTreeSelector> openApiGroups = new();
 | 
			
		||||
            var controllerTypes = App.EffectiveTypes
 | 
			
		||||
            .Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass && u.IsDefined(typeof(OpenApiPermissionAttribute), false));
 | 
			
		||||
            foreach (var controller in controllerTypes)
 | 
			
		||||
            {
 | 
			
		||||
                var GroupName = controller.GetCustomAttribute<ApiDescriptionSettingsAttribute>().GroupName;
 | 
			
		||||
                if (GroupName == CateGoryConst.ThingsGatewayOpenApi)
 | 
			
		||||
                {
 | 
			
		||||
                    var Description = controller.GetCustomAttribute<DescriptionAttribute>().Description;
 | 
			
		||||
                    var parid = YitIdHelper.NextId();
 | 
			
		||||
                    OpenApiPermissionTreeSelector openApiGroup = new() { ApiName = Description, Id = parid, PermissionName = Description };
 | 
			
		||||
                    var routeName = "/" + controller.GetCustomAttribute<RouteAttribute>().Template;
 | 
			
		||||
                    //获取所有方法
 | 
			
		||||
                    var menthods = controller.GetMethods();
 | 
			
		||||
                    //遍历方法
 | 
			
		||||
                    foreach (var menthod in menthods)
 | 
			
		||||
                    {
 | 
			
		||||
                        //获取忽略数据权限特性
 | 
			
		||||
                        var ignoreOpenApiPermission = menthod.GetCustomAttribute<IgnoreOpenApiPermissionAttribute>();
 | 
			
		||||
                        if (ignoreOpenApiPermission == null)//如果是空的代表需要数据权限
 | 
			
		||||
                        {
 | 
			
		||||
                            //获取接口描述
 | 
			
		||||
                            var description = menthod.GetCustomAttribute<DescriptionAttribute>();
 | 
			
		||||
                            if (description != null)
 | 
			
		||||
                            {
 | 
			
		||||
                                //默认路由名称
 | 
			
		||||
                                var apiRoute = menthod.Name;
 | 
			
		||||
                                //获取get特性
 | 
			
		||||
                                var requestGet = menthod.GetCustomAttribute<HttpGetAttribute>();
 | 
			
		||||
                                if (requestGet != null)//如果是get方法
 | 
			
		||||
                                    apiRoute = requestGet.Template;
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    //获取post特性
 | 
			
		||||
                                    var requestPost = menthod.GetCustomAttribute<HttpPostAttribute>();
 | 
			
		||||
                                    if (requestPost != null)//如果是post方法
 | 
			
		||||
                                        apiRoute = requestPost.Template;
 | 
			
		||||
                                }
 | 
			
		||||
                                apiRoute = routeName + $"/{apiRoute}";
 | 
			
		||||
                                var apiName = description.Description;//如果描述不为空则接口名称用描述的名称
 | 
			
		||||
 | 
			
		||||
                                //合并
 | 
			
		||||
                                var permissionName = apiRoute + $"[{apiName}]";
 | 
			
		||||
                                //添加到权限列表
 | 
			
		||||
                                openApiGroup.Children.Add(new OpenApiPermissionTreeSelector
 | 
			
		||||
                                {
 | 
			
		||||
                                    Id = YitIdHelper.NextId(),
 | 
			
		||||
                                    ParentId = parid,
 | 
			
		||||
                                    ApiName = apiName,
 | 
			
		||||
                                    ApiRoute = apiRoute,
 | 
			
		||||
                                    PermissionName = permissionName
 | 
			
		||||
                                });
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    openApiGroups.Add(openApiGroup);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return openApiGroups;
 | 
			
		||||
        }, false);
 | 
			
		||||
        return displayName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取全部页面权限内容
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="routers"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static List<PermissionTreeSelector> PermissionTreeSelector(List<string> routers)
 | 
			
		||||
    {
 | 
			
		||||
        var cacheKey = $"{nameof(PermissionTreeSelector)}-{CultureInfo.CurrentUICulture.Name}-{routers.ToJsonString()}";
 | 
			
		||||
        List<PermissionTreeSelector> displayName = CacheStatic.Cache.GetOrCreate(cacheKey, entry =>
 | 
			
		||||
        {
 | 
			
		||||
            List<PermissionTreeSelector> permissions = new();//权限列表
 | 
			
		||||
 | 
			
		||||
            // 获取所有需要数据权限的控制器
 | 
			
		||||
            var controllerTypes = App.EffectiveTypes.
 | 
			
		||||
                Where(u => !u.IsInterface && !u.IsAbstract && u.IsClass
 | 
			
		||||
                && u.IsDefined(typeof(AuthorizeAttribute), false)
 | 
			
		||||
                && u.IsDefined(typeof(Microsoft.AspNetCore.Components.RouteAttribute), false));
 | 
			
		||||
            foreach (var controller in controllerTypes)
 | 
			
		||||
            {
 | 
			
		||||
                //获取数据权限特性
 | 
			
		||||
                var routeName = controller.GetCustomAttribute<Microsoft.AspNetCore.Components.RouteAttribute>()?.Template;
 | 
			
		||||
                if (routeName == null)
 | 
			
		||||
                    continue;
 | 
			
		||||
                if (routers.Contains(routeName))
 | 
			
		||||
                {
 | 
			
		||||
                    var apiRoute = $"{routeName}";
 | 
			
		||||
                    permissions.Add(new() { ApiRoute = apiRoute });
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return permissions;
 | 
			
		||||
        }, false
 | 
			
		||||
        );
 | 
			
		||||
        return displayName;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
#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.Text;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 种子数据工具类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SeedDataUtil
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// json转化为种子列表
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <typeparam name="T"></typeparam>
 | 
			
		||||
    /// <param name="jsonName"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static List<T> GetSeedData<T>(string jsonName)
 | 
			
		||||
    {
 | 
			
		||||
        var seedData = new List<T>();//种子数据结果
 | 
			
		||||
        var basePath = AppContext.BaseDirectory;//获取项目目录
 | 
			
		||||
        var json = basePath.CombinePath("SeedData", "Json", jsonName);//获取文件路径
 | 
			
		||||
        var dataString = ReadFile(json);//读取文件
 | 
			
		||||
        if (!string.IsNullOrEmpty(dataString))//如果有内容
 | 
			
		||||
        {
 | 
			
		||||
            //字段没有数据的替换成null
 | 
			
		||||
            dataString = dataString.Replace("\"\"", "null");
 | 
			
		||||
            //将json字符串转为实体,这里extjson可以正常转换为字符串
 | 
			
		||||
            var seedDataRecord = dataString.ToJsonWithT<SeedDataRecords<T>>();
 | 
			
		||||
 | 
			
		||||
            //遍历seedDataRecord
 | 
			
		||||
            for (int i = 0; i < seedDataRecord.Records.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                #region 处理ExtJosn
 | 
			
		||||
 | 
			
		||||
                //获取extjson属性
 | 
			
		||||
                var propertyExtJosn = typeof(T).GetProperty(nameof(PrimaryKeyEntity.ExtJson));
 | 
			
		||||
                if (propertyExtJosn != null)
 | 
			
		||||
                {
 | 
			
		||||
                    //获取extjson的值
 | 
			
		||||
                    var extJson = propertyExtJosn.GetValue(seedDataRecord.Records[i])?.ToString();
 | 
			
		||||
                    // 如果extjson不为空并且包含NullableDictionary表示序列化失败了
 | 
			
		||||
                    if (!string.IsNullOrEmpty(extJson) && extJson.Contains("NullableDictionary"))
 | 
			
		||||
                    {
 | 
			
		||||
                        //设置extjson为seedDataRecord对应的值
 | 
			
		||||
                        extJson = propertyExtJosn.GetValue(seedDataRecord.Records[i])?.ToString();
 | 
			
		||||
                        //seedDataRecord赋值seedDataRecord的extjson
 | 
			
		||||
                        propertyExtJosn.SetValue(seedDataRecord.Records[i], extJson);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                #endregion 处理ExtJosn
 | 
			
		||||
 | 
			
		||||
                #region 处理ConfigValue
 | 
			
		||||
 | 
			
		||||
                //获取extjson属性
 | 
			
		||||
                var propertyConfigValue = typeof(T).GetProperty(nameof(SysConfig.ConfigValue));
 | 
			
		||||
                if (propertyConfigValue != null)
 | 
			
		||||
                {
 | 
			
		||||
                    //获取extjson的值
 | 
			
		||||
                    var configValue = propertyConfigValue.GetValue(seedDataRecord.Records[i])?.ToString();
 | 
			
		||||
                    // 如果extjson不为空并且包含NullableDictionary表示序列化失败了
 | 
			
		||||
                    if (!string.IsNullOrEmpty(configValue) && configValue.Contains("NullableDictionary"))
 | 
			
		||||
                    {
 | 
			
		||||
                        //设置extjson为seedDataRecord对应的值
 | 
			
		||||
                        configValue = propertyConfigValue.GetValue(seedDataRecord.Records[i])?.ToString();
 | 
			
		||||
                        //seedDataRecord赋值seedDataRecord的extjson
 | 
			
		||||
                        propertyConfigValue.SetValue(seedDataRecord.Records[i], configValue);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                #endregion 处理ConfigValue
 | 
			
		||||
            }
 | 
			
		||||
            //种子数据赋值
 | 
			
		||||
            seedData = seedDataRecord.Records;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return seedData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 读取文件
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="Path"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public static string ReadFile(string Path)
 | 
			
		||||
    {
 | 
			
		||||
        if (!File.Exists(Path))
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
 | 
			
		||||
        StreamReader streamReader = new(Path, Encoding.GetEncoding("utf-8"));
 | 
			
		||||
        string result = streamReader.ReadToEnd();
 | 
			
		||||
        streamReader.Close();
 | 
			
		||||
        streamReader.Dispose();
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 种子数据格式实体类,遵循Navicat导出json格式
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="T"></typeparam>
 | 
			
		||||
 | 
			
		||||
public class SeedDataRecords<T>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public List<T> Records { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
#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 UAParser;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 单例Parser
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class StaticParser
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 单例
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static Parser Parser { get; } = Parser.GetDefault();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
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(IServiceProvider serviceProvider)
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope = serviceProvider.CreateScope();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业调度服务启动时
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public IEnumerable<SchedulerBuilder> Preload()
 | 
			
		||||
    {
 | 
			
		||||
        // 获取所有定义的作业
 | 
			
		||||
        var allJobs = App.EffectiveTypes.ScanToBuilders().ToList();
 | 
			
		||||
        return allJobs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 作业计划初始化通知
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="builder"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    public SchedulerBuilder OnLoading(SchedulerBuilder builder)
 | 
			
		||||
    {
 | 
			
		||||
        return builder;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void Dispose()
 | 
			
		||||
    {
 | 
			
		||||
        _serviceScope?.Dispose();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void OnChanged(PersistenceContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public void OnTriggerChanged(PersistenceTriggerContext context)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 = 30; // 删除30天以前
 | 
			
		||||
        await db.Deleteable<SysVisitLog>().Where(u => u.CreateTime < SysDateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除访问日志
 | 
			
		||||
        await db.Deleteable<SysOperateLog>().Where(u => u.CreateTime < SysDateTimeExtensions.CurrentDateTime.AddDays(-daysAgo)).ExecuteCommandAsync(); // 删除操作日志
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录事件参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginOpenApiEvent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime DateTime = SysDateTimeExtensions.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
        {
 | 
			
		||||
            CacheStatic.Cache.Set(CacheConst.CACHE_OPENAPIUSER + openApiUser.Id, openApiUser, false); //更新Cache信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
#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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
#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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
#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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,173 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
using Furion.DataEncryption;
 | 
			
		||||
using Furion.DependencyInjection;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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.ToBoolean();//如果配置不为空则设置单用户登录选项为系统配置的值
 | 
			
		||||
 | 
			
		||||
            //判断是否单用户登录
 | 
			
		||||
            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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,57 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 = true)]
 | 
			
		||||
    public int VerificatCount { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌信息集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌列表")]
 | 
			
		||||
    public List<VerificatInfo> VerificatSignList { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<OpenApiSessionOutput>> PageAsync(OpenApiSessionPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,108 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
 | 
			
		||||
    {
 | 
			
		||||
        verificatInfos = verificatInfos.ToList();
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            var now = SysDateTimeExtensions.CurrentDateTime;
 | 
			
		||||
            it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
 | 
			
		||||
            var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
 | 
			
		||||
            var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SqlSugarPagedList<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)
 | 
			
		||||
                {
 | 
			
		||||
                    GetVerificatInfos(ref verificatInfos);//获取剩余时间
 | 
			
		||||
                    it.VerificatCount = verificatInfos.Count;//令牌数量
 | 
			
		||||
                    it.VerificatSignList = verificatInfos;//令牌列表
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    it.VerificatSignList = new();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            });
 | 
			
		||||
        for (int i = 0; i < input.SortField.Count; 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,150 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,106 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,280 +0,0 @@
 | 
			
		||||
#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 SqlSugar;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="IOpenApiUserService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class OpenApiUserService : DbRepository<OpenApiUser>, IOpenApiUserService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IOpenApiUserService"/>
 | 
			
		||||
    public OpenApiUserService(
 | 
			
		||||
        IVerificatService verificatService
 | 
			
		||||
                      )
 | 
			
		||||
    {
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <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 = CacheStatic.Cache.Get<OpenApiUser>(CacheConst.CACHE_OPENAPIUSER + item, false);//获取用户列表
 | 
			
		||||
            openApiUsers.Add(user);
 | 
			
		||||
            CacheStatic.Cache.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)
 | 
			
		||||
            {
 | 
			
		||||
                //删除账号
 | 
			
		||||
                CacheStatic.Cache.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 = CacheStatic.Cache.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
 | 
			
		||||
                CacheStatic.Cache.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 = CacheStatic.Cache.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
 | 
			
		||||
                CacheStatic.Cache.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<SqlSugarPagedList<OpenApiUser>> PageAsync(OpenApiUserPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<OpenApiUser>()
 | 
			
		||||
         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => u.Account.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
        for (int i = 0; i < input.SortField.Count; 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}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,104 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "RECORDS": [
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222222",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_SWAGGER_NAME",
 | 
			
		||||
      "ConfigValue": "admin",
 | 
			
		||||
      "Remark": "swagger账号",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222223",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_SWAGGER_PASSWORD",
 | 
			
		||||
      "ConfigValue": "123456",
 | 
			
		||||
      "Remark": "swagger密码",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222224",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_SWAGGERLOGIN_OPEN",
 | 
			
		||||
      "ConfigValue": "false",
 | 
			
		||||
      "Remark": "swagger开启登录",
 | 
			
		||||
      "SortCode": "3",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222226",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_TITLE",
 | 
			
		||||
      "ConfigValue": "ThingsGateway",
 | 
			
		||||
      "Remark": "标题",
 | 
			
		||||
      "SortCode": "5",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222228",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_COPYRIGHT",
 | 
			
		||||
      "ConfigValue": "ThingsGateway ©2023 Diego",
 | 
			
		||||
      "Remark": "系统版权",
 | 
			
		||||
      "SortCode": "6",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222229",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_COPYRIGHT_URL",
 | 
			
		||||
      "ConfigValue": "https://gitee.com/diego2098/ThingsGateway",
 | 
			
		||||
      "Remark": "系统版权链接地址",
 | 
			
		||||
      "SortCode": "7",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222231",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_PASSWORD",
 | 
			
		||||
      "ConfigValue": "111111",
 | 
			
		||||
      "Remark": "默认用户密码",
 | 
			
		||||
      "SortCode": "8",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222227",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_VERIFICAT_EXPIRES",
 | 
			
		||||
      "ConfigValue": "14400",
 | 
			
		||||
      "Remark": "Verificat过期时间(分)",
 | 
			
		||||
      "SortCode": "9",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222232",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_SINGLE_OPEN",
 | 
			
		||||
      "ConfigValue": "false",
 | 
			
		||||
      "Remark": "单用户登录开关",
 | 
			
		||||
      "SortCode": "10",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222230",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_CAPTCHA_OPEN",
 | 
			
		||||
      "ConfigValue": "true",
 | 
			
		||||
      "Remark": "登录验证码开关",
 | 
			
		||||
      "SortCode": "11",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "22222222222225",
 | 
			
		||||
      "Category": "SYS_CONFIGBASEDEFAULT",
 | 
			
		||||
      "ConfigKey": "CONFIG_REMARK",
 | 
			
		||||
      "ConfigValue": "边缘采集网关",
 | 
			
		||||
      "Remark": "说明",
 | 
			
		||||
      "SortCode": "12",
 | 
			
		||||
      "IsDelete": "false"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "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
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,564 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "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"
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "RECORDS": [
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "212725263001001",
 | 
			
		||||
      "Code": "superAdmin",
 | 
			
		||||
      "Name": "超级管理员",
 | 
			
		||||
      "SortCode": "1"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "212725263001002",
 | 
			
		||||
      "Code": "admin",
 | 
			
		||||
      "Name": "业务管理员",
 | 
			
		||||
      "SortCode": "2"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,34 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "RECORDS": [
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "212725263002001",
 | 
			
		||||
      "Account": "superAdmin",
 | 
			
		||||
      "LastLoginDevice": "PC",
 | 
			
		||||
      "LastLoginIp": "0.0.0.1",
 | 
			
		||||
      "LastLoginTime": "2023-03-03 21:18:43.7092169",
 | 
			
		||||
      "LatestLoginDevice": "PC",
 | 
			
		||||
      "LatestLoginIp": "0.0.0.1",
 | 
			
		||||
      "LatestLoginTime": "2023-03-03 21:19:16.1043309",
 | 
			
		||||
      "Password": "7DA385A25A98388E",
 | 
			
		||||
      "SortCode": "1",
 | 
			
		||||
      "UserEnable": "true",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-03-03 21:19:16.1202211"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "Id": "201725263002001",
 | 
			
		||||
      "Account": "admin",
 | 
			
		||||
      "LastLoginDevice": "PC",
 | 
			
		||||
      "LastLoginIp": "0.0.0.1",
 | 
			
		||||
      "LastLoginTime": "2023-03-03 18:20:49.1875384",
 | 
			
		||||
      "LatestLoginDevice": "PC",
 | 
			
		||||
      "LatestLoginIp": "0.0.0.1",
 | 
			
		||||
      "LatestLoginTime": "2023-03-03 18:23:08.6424099",
 | 
			
		||||
      "Password": "7DA385A25A98388E",
 | 
			
		||||
      "SortCode": "2",
 | 
			
		||||
      "UserEnable": "true",
 | 
			
		||||
      "IsDelete": "false",
 | 
			
		||||
      "UpdateTime": "2023-03-03 18:23:08.6727296"
 | 
			
		||||
    }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 系统配置种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysConfigSeedData : ISqlSugarEntitySeedData<SysConfig>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysConfig> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysConfig>("sys_config.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 关系表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysRelationSeedData : ISqlSugarEntitySeedData<SysRelation>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysRelation> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysRelation>("sys_relation.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 资源表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysResourceSeedData : ISqlSugarEntitySeedData<SysResource>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysResource> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysResource>("sys_resource.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 角色种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysRoleSeedData : ISqlSugarEntitySeedData<SysRole>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysRole> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysRole>("sys_role.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户表种子数据
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class SysUserSeedData : ISqlSugarEntitySeedData<SysUser>
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<SysUser> SeedData()
 | 
			
		||||
    {
 | 
			
		||||
        return SeedDataUtil.GetSeedData<SysUser>("sys_user.json");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,118 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
using Furion.InstantMessaging;
 | 
			
		||||
using Furion.Logging.Extensions;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Http.Connections.Features;
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="ISysHub"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
[MapHub(HubConst.SysHubUrl)]
 | 
			
		||||
public class SysHub : Hub<ISysHub>
 | 
			
		||||
{
 | 
			
		||||
    readonly ILogger<ISysHub> _logger;
 | 
			
		||||
    /// <inheritdoc cref="ISysHub"/>
 | 
			
		||||
    public SysHub(ILogger<ISysHub> logger)
 | 
			
		||||
    {
 | 
			
		||||
        _logger = logger;
 | 
			
		||||
    }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 分隔符
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public const string SYS_TrackingCircuitHandlerid = "SYS_TrackingCircuitHandlerid";
 | 
			
		||||
 | 
			
		||||
    /// <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(SYS_TrackingCircuitHandlerid)[0].ToLong();//分割取第一个
 | 
			
		||||
        if (userId > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var _verificatService = App.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);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            if (isConnect)
 | 
			
		||||
                _logger.LogWarning($"未认证SignalR ID:{userIdentifier} 登录");
 | 
			
		||||
            else
 | 
			
		||||
                _logger.LogWarning($"未认证SignalR ID:{userIdentifier} 注销");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,43 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// AppStartup启动类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class Startup : AppStartup
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 配置
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public void ConfigureServices(IServiceCollection services)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // 任务调度
 | 
			
		||||
        services.AddSchedule(options =>
 | 
			
		||||
        {
 | 
			
		||||
            options.AddPersistence<JobPersistence>();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //事件总线
 | 
			
		||||
        services.AddEventBus();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,55 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
        {
 | 
			
		||||
            CacheStatic.Cache.Set(CacheConst.CACHE_SYSUSER + sysUser.Id, sysUser, false); //更新Cache信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
#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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,240 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
using Furion.DataEncryption;
 | 
			
		||||
using Furion.EventBus;
 | 
			
		||||
using Furion.FriendlyException;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.Authentication;
 | 
			
		||||
using Microsoft.AspNetCore.Authentication.Cookies;
 | 
			
		||||
using Microsoft.AspNetCore.Http;
 | 
			
		||||
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 ISysUserService _userService;
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IAuthService"/>
 | 
			
		||||
    public AuthService(
 | 
			
		||||
                       IEventPublisher eventPublisher,
 | 
			
		||||
                       ISysUserService userService,
 | 
			
		||||
                       IConfigService configService,
 | 
			
		||||
                       IVerificatService verificatService,
 | 
			
		||||
                        INoticeService noticeService
 | 
			
		||||
        )
 | 
			
		||||
    {
 | 
			
		||||
        _eventPublisher = eventPublisher;
 | 
			
		||||
        _userService = userService;
 | 
			
		||||
        _configService = configService;
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
        _noticeService = noticeService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public ValidCodeOutput GetCaptchaInfo()
 | 
			
		||||
    {
 | 
			
		||||
        //生成验证码
 | 
			
		||||
        var captchInfo = new Random().Next(1111, 9999).ToString();
 | 
			
		||||
        //生成请求号,并将验证码放入cache
 | 
			
		||||
        var reqNo = YitIdHelper.NextId();
 | 
			
		||||
        //插入cache
 | 
			
		||||
        CacheStatic.Cache.Set(CacheConst.LOGIN_CAPTCHA + reqNo, captchInfo, TimeSpan.FromMinutes(1), false);
 | 
			
		||||
        //返回验证码和请求号
 | 
			
		||||
        return new ValidCodeOutput { CodeValue = captchInfo, ValidCodeReqNo = reqNo };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public Task<SysUser> GetLoginUserAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return _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.ToBoolean())//如果需要验证码
 | 
			
		||||
            {
 | 
			
		||||
                //如果没填验证码,提示验证码不能为空
 | 
			
		||||
                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);//获取用户信息
 | 
			
		||||
        if (userInfo == null) 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 static void ValidValidCode(string validCode, long validCodeReqNo, bool isDelete = true)
 | 
			
		||||
    {
 | 
			
		||||
        var code = CacheStatic.Cache.Get<string>(CacheConst.LOGIN_CAPTCHA + validCodeReqNo, false);//从cache拿数据
 | 
			
		||||
        if (isDelete) CacheStatic.Cache.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 = SysDateTimeExtensions.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.ToBoolean();//如果配置不为空则设置单用户登录选项为系统配置的值
 | 
			
		||||
            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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
#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,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 登录事件参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class LoginEvent
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 时间
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public DateTime DateTime = SysDateTimeExtensions.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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,46 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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();
 | 
			
		||||
}
 | 
			
		||||
@@ -1,166 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
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.ToJsonWithT<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<SqlSugarPagedList<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 = 0; i < input.SortField.Count; 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 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,53 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<SysResource>> PageAsync(ButtonPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,132 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IConfigService"/>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class ConfigService : DbRepository<SysConfig>, IConfigService
 | 
			
		||||
{
 | 
			
		||||
    /// <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 = CacheStatic.Cache.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)
 | 
			
		||||
            {
 | 
			
		||||
                CacheStatic.Cache.Set(CacheConst.SYS_CONFIGCATEGORY + category, configList, true);//如果不为空,插入cache
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return configList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SqlSugarPagedList<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 = 0; i < input.SortField.Count; 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)
 | 
			
		||||
    {
 | 
			
		||||
        CacheStatic.Cache.Remove(CacheConst.SYS_CONFIGCATEGORY + category);//cache删除
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,67 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<SysConfig>> PageAsync(ConfigPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
#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: "文件格式错误");
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,93 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,168 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,32 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using Microsoft.AspNetCore.SignalR;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// <inheritdoc cref="INoticeService"/>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class NoticeService : INoticeService
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task LogoutAsync(long userId, List<VerificatInfo> verificatInfos, string message)
 | 
			
		||||
    {
 | 
			
		||||
        //客户端ID列表
 | 
			
		||||
        var clientIds = new List<string>();
 | 
			
		||||
        //遍历token列表获取客户端ID列表
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            clientIds.AddRange(it.ClientIds);
 | 
			
		||||
        });
 | 
			
		||||
        //获取signalr实例
 | 
			
		||||
        var signalr = App.GetService<IHubContext<SysHub, ISysHub>>();
 | 
			
		||||
        //发送其他客户端登录消息
 | 
			
		||||
        await signalr.Clients.Users(clientIds).Logout(message);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +0,0 @@
 | 
			
		||||
#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
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -1,49 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<SysOperateLog>> PageAsync(OperateLogPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,104 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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().GetPropertiesWithCache();
 | 
			
		||||
            Dictionary<string, object> devExport = new();
 | 
			
		||||
            foreach (var item in data)
 | 
			
		||||
            {
 | 
			
		||||
                //描述
 | 
			
		||||
                var desc = TypeExtensions.FindDisplayAttribute(item);
 | 
			
		||||
                //数据源增加
 | 
			
		||||
                devExport.Add(desc ?? item.Name, item.GetValue(devData)?.ToString());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            devExports.Add(devExport);
 | 
			
		||||
 | 
			
		||||
            #endregion
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        sheets.Add("操作日志", devExports);
 | 
			
		||||
 | 
			
		||||
        var memoryStream = new MemoryStream();
 | 
			
		||||
        await memoryStream.SaveAsAsync(sheets);
 | 
			
		||||
        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<SqlSugarPagedList<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(!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 = 0; i < input.SortField.Count; 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,99 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,145 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IRelationService"/>
 | 
			
		||||
public class RelationService : DbRepository<SysRelation>, IRelationService
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<SysRelation>> GetRelationByCategoryAsync(string category)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var sysRelations = CacheStatic.Cache.Get<List<SysRelation>>(CacheConst.CACHE_SYSRELATION + category, true);
 | 
			
		||||
        if (sysRelations == null)
 | 
			
		||||
        {
 | 
			
		||||
            //cache没有就去数据库拿
 | 
			
		||||
            sysRelations = await GetListAsync(it => it.Category == category);
 | 
			
		||||
            if (sysRelations.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                CacheStatic.Cache.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)
 | 
			
		||||
    {
 | 
			
		||||
        CacheStatic.Cache.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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
#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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,105 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取PageTabItems
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="nav"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    List<PageTabItem> SameLevelMenuPasePageTab(List<SysResource> nav);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,288 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Tab表示类
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <param name="Title">标题</param>
 | 
			
		||||
/// <param name="Href">跳转类型</param>
 | 
			
		||||
/// <param name="Icon">图标</param>
 | 
			
		||||
public record PageTabItem(string Title, string Href, string Icon);
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IResourceService"/>
 | 
			
		||||
public class ResourceService : DbRepository<SysResource>, IResourceService
 | 
			
		||||
{
 | 
			
		||||
    /// <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 = CacheStatic.Cache.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
 | 
			
		||||
                CacheStatic.Cache.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
 | 
			
		||||
            CacheStatic.Cache.Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.SPA.ToString());
 | 
			
		||||
            CacheStatic.Cache.Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.BUTTON.ToString());
 | 
			
		||||
            CacheStatic.Cache.Remove(CacheConst.CACHE_SYSRESOURCE + ResourceCategoryEnum.MENU.ToString());
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            //否则只删除一个Key
 | 
			
		||||
            CacheStatic.Cache.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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public List<PageTabItem> SameLevelMenuPasePageTab(List<SysResource> nav)
 | 
			
		||||
    {
 | 
			
		||||
        List<PageTabItem> pageTabItems = new();
 | 
			
		||||
        if (nav == null) return pageTabItems;
 | 
			
		||||
        foreach (var item in nav)
 | 
			
		||||
        {
 | 
			
		||||
            if ((item.Category == ResourceCategoryEnum.MENU || item.Category == ResourceCategoryEnum.SPA) && item.TargetType == TargetTypeEnum.SELF)
 | 
			
		||||
            {
 | 
			
		||||
                if (item.Icon == null)
 | 
			
		||||
                    pageTabItems.Add(new PageTabItem(item.Title, item.Component, ""));
 | 
			
		||||
                else
 | 
			
		||||
                    pageTabItems.Add(new PageTabItem(item.Title, item.Component, item.Icon));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return pageTabItems;
 | 
			
		||||
    }
 | 
			
		||||
    /// <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;//原路返回
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
@@ -1,107 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<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);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,406 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc cref="IRoleService"/>
 | 
			
		||||
        public RoleService(
 | 
			
		||||
                           IRelationService relationService,
 | 
			
		||||
                           IResourceService resourceService,
 | 
			
		||||
                           IEventPublisher eventPublisher)
 | 
			
		||||
        {
 | 
			
		||||
            _relationService = relationService;
 | 
			
		||||
            _resourceService = resourceService;
 | 
			
		||||
            _eventPublisher = eventPublisher;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <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 = CacheStatic.Cache.Get<List<SysRole>>(CacheConst.CACHE_SYSROLE, true);
 | 
			
		||||
            if (sysRoles == null)
 | 
			
		||||
            {
 | 
			
		||||
                //cache没有就去数据库拿
 | 
			
		||||
                sysRoles = await base.GetListAsync();
 | 
			
		||||
                if (sysRoles.Count > 0)
 | 
			
		||||
                {
 | 
			
		||||
                    //插入Cache
 | 
			
		||||
                    CacheStatic.Cache.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.ToJsonWithT<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<SqlSugarPagedList<SysRole>> PageAsync(RolePageInput input)
 | 
			
		||||
        {
 | 
			
		||||
            var query = Context.Queryable<SysRole>()
 | 
			
		||||
                             .WhereIF(!string.IsNullOrEmpty(input.SearchKey), it => it.Name.Contains(input.SearchKey));//根据关键字查询
 | 
			
		||||
            for (int i = 0; i < input.SortField.Count; 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()
 | 
			
		||||
        {
 | 
			
		||||
            CacheStatic.Cache.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 方法
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,64 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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 = true)]
 | 
			
		||||
    public int VerificatCount { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 令牌信息集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("令牌列表")]
 | 
			
		||||
    public List<VerificatInfo> VerificatSignList { get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<SessionOutput>> PageAsync(SessionPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,118 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<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)
 | 
			
		||||
                  {
 | 
			
		||||
                      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 = 0; i < input.SortField.Count; 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 void GetVerificatInfos(ref List<VerificatInfo> verificatInfos)
 | 
			
		||||
    {
 | 
			
		||||
        verificatInfos.ForEach(it =>
 | 
			
		||||
        {
 | 
			
		||||
            var now = SysDateTimeExtensions.CurrentDateTime;
 | 
			
		||||
            it.VerificatRemain = now.GetDiffTime(it.VerificatTimeout);//获取时间差
 | 
			
		||||
            var verificatSecond = it.VerificatTimeout.AddMinutes(-it.Expire).ToLong();//颁发时间转为时间戳
 | 
			
		||||
            var timeoutSecond = it.VerificatTimeout.ToLong();//过期时间转为时间戳
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<SysResource>> PageAsync(SpaPageInput input);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,127 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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<SqlSugarPagedList<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 = 0; i < input.SortField.Count; 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 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
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; }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,35 +0,0 @@
 | 
			
		||||
#region copyright
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
//  此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
 | 
			
		||||
//  此代码版权(除特别声明外的代码)归作者本人Diego所有
 | 
			
		||||
//  源代码使用协议遵循本仓库的开源协议及附加协议
 | 
			
		||||
//  Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
 | 
			
		||||
//  Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
 | 
			
		||||
//  使用文档:https://diego2098.gitee.io/thingsgateway-docs/
 | 
			
		||||
//  QQ群:605534569
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 选择用户输出参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UserSelectorOutput
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 账号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Account { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long Id { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 姓名
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public string Name { get; set; }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 用户服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface ISysUserService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 添加用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">添加参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task AddAsync(UserAddInput 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(UserEditInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 启用用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户Id</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EnableUserAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据用户ID获取按钮ID集合
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<string>> GetButtonCodeListAsync(long userId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///根据用户账号获取用户ID
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="account">用户账号</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<long> GetIdByAccountAsync(string account);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据账号获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="account">用户名</param>
 | 
			
		||||
    /// <returns>用户信息</returns>
 | 
			
		||||
    Task<SysUser> GetUserByAccountAsync(string account);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 根据ID获取用户信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="Id">用户ID</param>
 | 
			
		||||
    /// <returns>用户信息</returns>
 | 
			
		||||
    Task<SysUser> GetUserByIdAsync(long Id);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 给用户授权角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">授权参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task GrantRoleAsync(UserGrantRoleInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取用户拥有角色
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户ID</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<long>> OwnRoleAsync(BaseIdInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户分页查询
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">查询参数</param>
 | 
			
		||||
    /// <returns>用户分页列表</returns>
 | 
			
		||||
    Task<SqlSugarPagedList<SysUser>> PageAsync(UserPageInput input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 重置密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">用户Id</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task ResetPasswordAsync(long input);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 用户选择器
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<UserSelectorOutput>> UserSelectorAsync(string searchKey);
 | 
			
		||||
}
 | 
			
		||||
@@ -1,418 +0,0 @@
 | 
			
		||||
#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 SqlSugar;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
using ThingsGateway.Admin.Core.JsonExtensions;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="ISysUserService"/>
 | 
			
		||||
[Injection(Proxy = typeof(OperDispatchProxy))]
 | 
			
		||||
public class SysUserService : DbRepository<SysUser>, ISysUserService
 | 
			
		||||
{
 | 
			
		||||
    private readonly IConfigService _configService;
 | 
			
		||||
    private readonly IRelationService _relationService;
 | 
			
		||||
    private readonly IResourceService _resourceService;
 | 
			
		||||
    private readonly IRoleService _roleService;
 | 
			
		||||
    private readonly IVerificatService _verificatService;
 | 
			
		||||
    /// <inheritdoc cref="ISysUserService"/>
 | 
			
		||||
    public SysUserService(
 | 
			
		||||
                       IRelationService relationService,
 | 
			
		||||
                       IResourceService resourceService,
 | 
			
		||||
                       IVerificatService verificatService,
 | 
			
		||||
                       IRoleService roleService,
 | 
			
		||||
                       IConfigService configService)
 | 
			
		||||
    {
 | 
			
		||||
        _relationService = relationService;
 | 
			
		||||
        _resourceService = resourceService;
 | 
			
		||||
        _roleService = roleService;
 | 
			
		||||
        _configService = configService;
 | 
			
		||||
        _verificatService = verificatService;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("添加用户")]
 | 
			
		||||
    public async Task AddAsync(UserAddInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var account_Id = await GetIdByAccountAsync(input.Account);
 | 
			
		||||
        if (account_Id > 0)
 | 
			
		||||
            throw Oops.Bah($"存在重复的账号:{input.Account}");
 | 
			
		||||
 | 
			
		||||
        var sysUser = input.Adapt<SysUser>();//实体转换
 | 
			
		||||
 | 
			
		||||
        //获取默认密码
 | 
			
		||||
        sysUser.Password = await GetDefaultPassWord();//设置密码
 | 
			
		||||
        sysUser.UserEnable = true;//默认状态
 | 
			
		||||
        await InsertAsync(sysUser);//添加数据
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("删除用户")]
 | 
			
		||||
    public async Task DeleteAsync(params long[] ids)
 | 
			
		||||
    {
 | 
			
		||||
        //获取所有ID
 | 
			
		||||
        if (ids.Length > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var containsSuperAdmin = await IsAnyAsync(it => it.Account == RoleConst.SuperAdmin && ids.Contains(it.Id));//判断是否有超管
 | 
			
		||||
            if (containsSuperAdmin)
 | 
			
		||||
                throw Oops.Bah($"不可删除系统内置超管用户");
 | 
			
		||||
            if (ids.Contains(UserManager.UserId))
 | 
			
		||||
                throw Oops.Bah($"不可删除自己");
 | 
			
		||||
 | 
			
		||||
            var result = await DeleteByIdsAsync(ids.Cast<object>().ToArray());
 | 
			
		||||
            if (result)
 | 
			
		||||
            {
 | 
			
		||||
                //从列表中删除
 | 
			
		||||
                foreach (var id in ids)
 | 
			
		||||
                {
 | 
			
		||||
                    await _verificatService.SetVerificatIdAsync(id, new());
 | 
			
		||||
                }
 | 
			
		||||
                DeleteUserFromCache(ids);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public void DeleteUserFromCache(params long[] ids)
 | 
			
		||||
    {
 | 
			
		||||
        List<SysUser> sysUsers = new();
 | 
			
		||||
        foreach (var item in ids)
 | 
			
		||||
        {
 | 
			
		||||
            var user = CacheStatic.Cache.Get<SysUser>(CacheConst.CACHE_SYSUSER + item, false);//获取用户列表
 | 
			
		||||
            sysUsers.Add(user);
 | 
			
		||||
            //删除账号
 | 
			
		||||
            CacheStatic.Cache.Remove(CacheConst.CACHE_SYSUSER + item);
 | 
			
		||||
        }
 | 
			
		||||
        sysUsers = sysUsers.Where(it => it != null).ToList();//过滤掉不存在的
 | 
			
		||||
        if (sysUsers.Count > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var accounts = sysUsers.Select(it => it.Account).ToArray();//账号集合
 | 
			
		||||
            foreach (var item in accounts)
 | 
			
		||||
            {
 | 
			
		||||
                //删除账号
 | 
			
		||||
                CacheStatic.Cache.Remove(CacheConst.CAHCE_SYSUSERACCOUNT + item);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("禁用用户")]
 | 
			
		||||
    public async Task DisableUserAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        var sysUser = await GetUserByIdAsync(input);//获取用户信息
 | 
			
		||||
        if (sysUser != null)
 | 
			
		||||
        {
 | 
			
		||||
            var isSuperAdmin = sysUser.Account == RoleConst.SuperAdmin;//判断是否有超管
 | 
			
		||||
            if (isSuperAdmin)
 | 
			
		||||
                throw Oops.Bah($"不可禁用系统内置超管用户账号");
 | 
			
		||||
            CheckSelf(input, AdminConst.Disable);//判断是不是自己
 | 
			
		||||
                                                 //设置状态为禁用
 | 
			
		||||
            if (await UpdateAsync(it => new SysUser { UserEnable = false }, it => it.Id == input))
 | 
			
		||||
            {
 | 
			
		||||
                //从列表中删除
 | 
			
		||||
                await _verificatService.SetVerificatIdAsync(input, new());
 | 
			
		||||
                DeleteUserFromCache(input);//从cache删除用户信息
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("编辑用户")]
 | 
			
		||||
    public async Task EditAsync(UserEditInput input)
 | 
			
		||||
    {
 | 
			
		||||
        await CheckInputAsync(input);//检查参数
 | 
			
		||||
        var exist = await GetUserByIdAsync(input.Id);//获取用户信息
 | 
			
		||||
        if (exist != null)
 | 
			
		||||
        {
 | 
			
		||||
            var isSuperAdmin = exist.Account == RoleConst.SuperAdmin;//判断是否有超管
 | 
			
		||||
            if (isSuperAdmin && !UserManager.IsSuperAdmin)
 | 
			
		||||
                throw Oops.Bah($"不可修改系统内置超管用户账号");
 | 
			
		||||
            var sysUser = input.Adapt<SysUser>();//实体转换
 | 
			
		||||
            if (await Context.Updateable(sysUser).IgnoreColumns(it =>
 | 
			
		||||
            new
 | 
			
		||||
            {
 | 
			
		||||
                //忽略更新字段
 | 
			
		||||
                it.Password,
 | 
			
		||||
                it.LastLoginDevice,
 | 
			
		||||
                it.LastLoginIp,
 | 
			
		||||
                it.LastLoginTime,
 | 
			
		||||
                it.LatestLoginDevice,
 | 
			
		||||
                it.LatestLoginIp,
 | 
			
		||||
                it.LatestLoginTime
 | 
			
		||||
            }).ExecuteCommandAsync() > 0)//修改数据
 | 
			
		||||
                DeleteUserFromCache(sysUser.Id);//用户缓存到cache
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("启用用户")]
 | 
			
		||||
    public async Task EnableUserAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        CheckSelf(input, AdminConst.Enable);//判断是不是自己
 | 
			
		||||
 | 
			
		||||
        //设置状态为启用
 | 
			
		||||
        if (await UpdateAsync(it => new SysUser { UserEnable = true }, it => it.Id == input))
 | 
			
		||||
            DeleteUserFromCache(input);//从cache删除用户信息
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<string>> GetButtonCodeListAsync(long userId)
 | 
			
		||||
    {
 | 
			
		||||
        List<string> buttonCodeList = new();//按钮ID集合
 | 
			
		||||
 | 
			
		||||
        //获取关系集合
 | 
			
		||||
        var roleList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);
 | 
			
		||||
        var roleIdList = roleList.Select(x => x.TargetId.ToLong()).ToList();//角色ID列表
 | 
			
		||||
        if (roleIdList.Count > 0)//如果该用户有角色
 | 
			
		||||
        {
 | 
			
		||||
            List<long> buttonIdList = new();//按钮ID集合
 | 
			
		||||
            var resourceList = await _relationService.GetRelationListByObjectIdListAndCategoryAsync(roleIdList, CateGoryConst.Relation_SYS_ROLE_HAS_RESOURCE);//获取资源集合
 | 
			
		||||
            resourceList.ForEach(it =>
 | 
			
		||||
            {
 | 
			
		||||
                if (!string.IsNullOrEmpty(it.ExtJson)) buttonIdList.AddRange(it.ExtJson.ToJsonWithT<RelationRoleResuorce>().ButtonInfo);//如果有按钮权限,将按钮ID放到buttonIdList
 | 
			
		||||
            });
 | 
			
		||||
            if (buttonIdList.Count > 0)
 | 
			
		||||
            {
 | 
			
		||||
                buttonCodeList = await _resourceService.GetCodeByIdsAsync(buttonIdList, ResourceCategoryEnum.BUTTON);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return buttonCodeList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<long> GetIdByAccountAsync(string account)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿
 | 
			
		||||
        var userId = CacheStatic.Cache.Get<long>(CacheConst.CAHCE_SYSUSERACCOUNT + account, false);
 | 
			
		||||
        if (userId == 0)
 | 
			
		||||
        {
 | 
			
		||||
            //单查获取用户账号对应ID
 | 
			
		||||
            userId = await GetFirstAsync(it => it.Account == account, it => it.Id);
 | 
			
		||||
            if (userId != 0)
 | 
			
		||||
            {
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                CacheStatic.Cache.Set(CacheConst.CAHCE_SYSUSERACCOUNT + account, userId, false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return userId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<string>> GetPermissionListByUserIdAsync(long userId)
 | 
			
		||||
    {
 | 
			
		||||
        var permissions = new List<string>();//权限集合
 | 
			
		||||
        var roleIdList = await _relationService.GetRelationListByObjectIdAndCategoryAsync(userId, CateGoryConst.Relation_SYS_USER_HAS_ROLE);//根据用户ID获取角色ID
 | 
			
		||||
        if (roleIdList.Count > 0)//如果角色ID不为空
 | 
			
		||||
        {
 | 
			
		||||
            //获取角色权限信息
 | 
			
		||||
            var sysRelations = await _relationService.GetRelationListByObjectIdListAndCategoryAsync(roleIdList.Select(it => it.TargetId.ToLong()).ToList(), CateGoryConst.Relation_SYS_ROLE_HAS_PERMISSION);
 | 
			
		||||
            var relationGroup = sysRelations.GroupBy(it => it.TargetId).ToList();//根据目标ID,也就是接口名分组,因为存在一个用户多个角色
 | 
			
		||||
 | 
			
		||||
            //遍历分组
 | 
			
		||||
            relationGroup.ForEach(it =>
 | 
			
		||||
            {
 | 
			
		||||
                HashSet<string> scopeSet = new();//定义不可重复列表
 | 
			
		||||
                var relationList = it.ToList();//关系列表
 | 
			
		||||
                relationList.ForEach(it =>
 | 
			
		||||
                {
 | 
			
		||||
                    var rolePermission = it.ExtJson.ToJsonWithT<RelationRolePermission>();
 | 
			
		||||
                    scopeSet.Add(rolePermission.ApiUrl);
 | 
			
		||||
                });
 | 
			
		||||
                permissions.AddRange(scopeSet);//将改URL的权限集合加入权限集合列表
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        return permissions;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SysUser> GetUserByAccountAsync(string account)
 | 
			
		||||
    {
 | 
			
		||||
        var userId = await GetIdByAccountAsync(account);//获取用户ID
 | 
			
		||||
        if (userId > 0)
 | 
			
		||||
        {
 | 
			
		||||
            var sysUser = await GetUserByIdAsync(userId);//获取用户信息
 | 
			
		||||
            if (sysUser.Account == account)//这里做了比较用来限制大小写
 | 
			
		||||
                return sysUser;
 | 
			
		||||
            else
 | 
			
		||||
                return null;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SysUser> GetUserByIdAsync(long Id)
 | 
			
		||||
    {
 | 
			
		||||
        //先从Cache拿,需要获取新的对象,避免操作导致缓存中对象改变
 | 
			
		||||
        var sysUser = CacheStatic.Cache.Get<SysUser>(CacheConst.CACHE_SYSUSER + Id.ToString(), true);
 | 
			
		||||
        if (sysUser == null)
 | 
			
		||||
        {
 | 
			
		||||
            sysUser = await Context.Queryable<SysUser>()
 | 
			
		||||
            .Where(u => u.Id == Id)
 | 
			
		||||
            .FirstAsync();
 | 
			
		||||
            if (sysUser != null)
 | 
			
		||||
            {
 | 
			
		||||
                //获取按钮码
 | 
			
		||||
                var buttonCodeList = await GetButtonCodeListAsync(sysUser.Id);
 | 
			
		||||
                //获取角色码
 | 
			
		||||
                var roleCodeList = await _roleService.GetRoleListByUserIdAsync(sysUser.Id);
 | 
			
		||||
                //获取权限码
 | 
			
		||||
                var permissionCodeList = await GetPermissionListByUserIdAsync(sysUser.Id);
 | 
			
		||||
 | 
			
		||||
                //权限码赋值
 | 
			
		||||
                sysUser.ButtonCodeList = buttonCodeList;
 | 
			
		||||
                sysUser.RoleCodeList = roleCodeList.Select(it => it.Code).ToList();
 | 
			
		||||
                sysUser.RoleIdList = roleCodeList.Select(it => it.Id).ToList();
 | 
			
		||||
                sysUser.PermissionCodeList = permissionCodeList;
 | 
			
		||||
                //插入Cache
 | 
			
		||||
                CacheStatic.Cache.Set(CacheConst.CACHE_SYSUSER + sysUser.Id.ToString(), sysUser, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return sysUser;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [OperDesc("用户授权")]
 | 
			
		||||
    public async Task GrantRoleAsync(UserGrantRoleInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var sysUser = await GetUserByIdAsync(input.Id);//获取用户信息
 | 
			
		||||
        if (sysUser != null)
 | 
			
		||||
        {
 | 
			
		||||
            var isSuperAdmin = sysUser.Account == RoleConst.SuperAdmin;//判断是否有超管
 | 
			
		||||
            if (isSuperAdmin)
 | 
			
		||||
                throw Oops.Bah($"不能给超管分配角色");
 | 
			
		||||
            CheckSelf(input.Id, AdminConst.GrantRole);//判断是不是自己
 | 
			
		||||
 | 
			
		||||
            //给用户赋角色
 | 
			
		||||
            await _relationService.SaveRelationBatchAsync(CateGoryConst.Relation_SYS_USER_HAS_ROLE, input.Id, input.RoleIdList.Select(it => it.ToString()).ToList(), null, true);
 | 
			
		||||
            DeleteUserFromCache(input.Id);//从cache删除用户信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<long>> OwnRoleAsync(BaseIdInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var relations = await _relationService.GetRelationListByObjectIdAndCategoryAsync(input.Id, CateGoryConst.Relation_SYS_USER_HAS_ROLE);
 | 
			
		||||
        return relations.Select(it => it.TargetId.ToLong()).ToList();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<SqlSugarPagedList<SysUser>> PageAsync(UserPageInput input)
 | 
			
		||||
    {
 | 
			
		||||
        var query = Context.Queryable<SysUser>()
 | 
			
		||||
         .WhereIF(input.Expression != null, input.Expression?.ToExpression())//动态查询
 | 
			
		||||
         .WhereIF(!string.IsNullOrEmpty(input.SearchKey), u => u.Account.Contains(input.SearchKey))//根据关键字查询
 | 
			
		||||
         .Mapper(u =>
 | 
			
		||||
         {
 | 
			
		||||
             u.Password = null;//密码清空
 | 
			
		||||
         });
 | 
			
		||||
        for (int i = 0; i < input.SortField.Count; 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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    [OperDesc("重置密码")]
 | 
			
		||||
    public async Task ResetPasswordAsync(long input)
 | 
			
		||||
    {
 | 
			
		||||
        var password = await GetDefaultPassWord(true);//获取默认密码,这里不走Aop所以需要加密一下
 | 
			
		||||
        //重置密码
 | 
			
		||||
        if (await UpdateAsync(it => new SysUser { Password = password }, it => it.Id == input))
 | 
			
		||||
        {
 | 
			
		||||
            //从列表中删除
 | 
			
		||||
            await _verificatService.SetVerificatIdAsync(input, new());
 | 
			
		||||
            DeleteUserFromCache(input);//从cache删除用户信息
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public async Task<List<UserSelectorOutput>> UserSelectorAsync(string searchKey)
 | 
			
		||||
    {
 | 
			
		||||
        var result = await Context.Queryable<SysUser>()
 | 
			
		||||
                         .WhereIF(!string.IsNullOrEmpty(searchKey), it => it.Account.Contains(searchKey))//根据关键字查询
 | 
			
		||||
                         .Select<UserSelectorOutput>()//映射成SysUserSelectorOutput
 | 
			
		||||
                         .ToListAsync();
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #region 方法
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查输入参数
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="sysUser"></param>
 | 
			
		||||
    private async Task CheckInputAsync(SysUser sysUser)
 | 
			
		||||
    {
 | 
			
		||||
        //判断账号重复,直接从cache拿
 | 
			
		||||
        var account_Id = await GetIdByAccountAsync(sysUser.Account);
 | 
			
		||||
        if (account_Id > 0 && account_Id != sysUser.Id)
 | 
			
		||||
            throw Oops.Bah($"存在重复的账号:{sysUser.Account}");
 | 
			
		||||
        //如果手机号不是空
 | 
			
		||||
        if (!string.IsNullOrEmpty(sysUser.Phone))
 | 
			
		||||
        {
 | 
			
		||||
            if (!sysUser.Phone.MatchPhoneNumber())//验证手机格式
 | 
			
		||||
                throw Oops.Bah($"手机号码:{sysUser.Phone} 格式错误");
 | 
			
		||||
            sysUser.Phone = DESCEncryption.Encrypt(sysUser.Phone, DESCKeyConst.DESCKey);
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        //如果邮箱不是空
 | 
			
		||||
        if (!string.IsNullOrEmpty(sysUser.Email))
 | 
			
		||||
        {
 | 
			
		||||
            var ismatch = sysUser.Email.MatchEmail();//验证邮箱格式
 | 
			
		||||
            if (!ismatch)
 | 
			
		||||
                throw Oops.Bah($"邮箱:{sysUser.Email} 格式错误");
 | 
			
		||||
            if (await IsAnyAsync(it => it.Email == sysUser.Email && it.Id != sysUser.Id))
 | 
			
		||||
                throw Oops.Bah($"存在重复的邮箱:{sysUser.Email}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 检查是否为自己
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private void CheckSelf(long id, string operate)
 | 
			
		||||
    {
 | 
			
		||||
        if (id == UserManager.UserId)//如果是自己
 | 
			
		||||
        {
 | 
			
		||||
            throw Oops.Bah($"禁止{operate}自己");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取默认密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    private async Task<string> GetDefaultPassWord(bool isSm4 = false)
 | 
			
		||||
    {
 | 
			
		||||
        //获取默认密码
 | 
			
		||||
        var defaultPassword = (await _configService.GetByConfigKeyAsync(ConfigConst.SYS_CONFIGBASEDEFAULT, ConfigConst.CONFIG_PASSWORD)).ConfigValue;
 | 
			
		||||
        return isSm4 ? DESCEncryption.Encrypt(defaultPassword, DESCKeyConst.DESCKey) : defaultPassword;//判断是否需要加密
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #endregion 方法
 | 
			
		||||
}
 | 
			
		||||
@@ -1,61 +0,0 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
using ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 编辑个人信息参数
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class UpdateInfoInput : SysUser
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Id
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [MinValue(1, ErrorMessage = "Id不能为空")]
 | 
			
		||||
    public override long Id { get; set; }
 | 
			
		||||
}
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 修改密码
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PasswordInfoInput : BaseIdInput, IValidatableObject
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 旧密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("旧密码")]
 | 
			
		||||
    [Required(ErrorMessage = "不能为空")]
 | 
			
		||||
    public string OldPassword { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 新密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("新密码")]
 | 
			
		||||
    [Required(ErrorMessage = "不能为空")]
 | 
			
		||||
    public string NewPassword { get; set; }
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 确认密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Description("确认密码")]
 | 
			
		||||
    [Required(ErrorMessage = "不能为空")]
 | 
			
		||||
    public string ConfirmPassword { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
 | 
			
		||||
    {
 | 
			
		||||
        if (NewPassword != ConfirmPassword)
 | 
			
		||||
            yield return new ValidationResult("两次密码不一致", new[] { nameof(ConfirmPassword) });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,69 +0,0 @@
 | 
			
		||||
#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 ThingsGateway.Admin.Core;
 | 
			
		||||
 | 
			
		||||
namespace ThingsGateway.Admin.Application;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// 个人信息中心服务
 | 
			
		||||
/// </summary>
 | 
			
		||||
public interface IUserCenterService : ITransient
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更改密码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task EditPasswordAsync(PasswordInfoInput input);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取个人主页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<long> GetLoginDefaultRazorAsync(long userId);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取个人首页快捷方式
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<long>> GetLoginWorkbenchAsync();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 获取个人菜单
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task<List<SysResource>> GetOwnMenuAsync(string UserAccount = null);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 设置个人主页
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="userId"></param>
 | 
			
		||||
    /// <param name="defalutRazor"></param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task UpdateUserDefaultRazorAsync(long userId, long defalutRazor);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 更新个人信息
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">信息参数</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task UpdateUserInfoAsync(UpdateInfoInput input);
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// 编辑个人工作台
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="input">工作台字符串</param>
 | 
			
		||||
    /// <returns></returns>
 | 
			
		||||
    Task UpdateWorkbenchAsync(List<long> input);
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user