mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	Compare commits
	
		
			1025 Commits
		
	
	
		
			v5.1.1
			...
			887d5e85d0
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					887d5e85d0 | ||
| 
						 | 
					8c603ff8d7 | ||
| 
						 | 
					a0831dda45 | ||
| 
						 | 
					336b2e8cc3 | ||
| 
						 | 
					cea4855f57 | ||
| 
						 | 
					9fc043b105 | ||
| 
						 | 
					d729c8ecde | ||
| 
						 | 
					b726a91cdb | ||
| 
						 | 
					05d5d9be2c | ||
| 
						 | 
					c40a8b2f0b | ||
| 
						 | 
					8232908b3f | ||
| 
						 | 
					1db0bc83b2 | ||
| 
						 | 
					74a0ec1ec3 | ||
| 
						 | 
					1228e8f3ea | ||
| 
						 | 
					737838d92f | ||
| 
						 | 
					c054029cfc | ||
| 
						 | 
					62bbd78033 | ||
| 
						 | 
					82a5ed632f | ||
| 
						 | 
					52ddccba3e | ||
| 
						 | 
					1a12aecd49 | ||
| 
						 | 
					777ae645c5 | ||
| 
						 | 
					21c87eee9a | ||
| 
						 | 
					0c8ac12e4d | ||
| 
						 | 
					cf871d9387 | ||
| 
						 | 
					90fb26fbf1 | ||
| 
						 | 
					fdfca0b33a | ||
| 
						 | 
					facd3e351f | ||
| 
						 | 
					a4ad56f0eb | ||
| 
						 | 
					b9e5914bab | ||
| 
						 | 
					553fca28a2 | ||
| 
						 | 
					97caabe0a2 | ||
| 
						 | 
					122f2770b2 | ||
| 
						 | 
					748c95b30f | ||
| 
						 | 
					e0672fc753 | ||
| 
						 | 
					5a1523564b | ||
| 
						 | 
					0c2fe34d92 | ||
| 
						 | 
					2dde42168f | ||
| 
						 | 
					a5c2093c76 | ||
| 
						 | 
					ea74803ccc | ||
| 
						 | 
					9df837f047 | ||
| 
						 | 
					d6758dc47b | ||
| 
						 | 
					5a8dc8e1cf | ||
| 
						 | 
					eac7f1b4e2 | ||
| 
						 | 
					3749e7e724 | ||
| 
						 | 
					b709bc0214 | ||
| 
						 | 
					a09414110e | ||
| 
						 | 
					053dc50c4d | ||
| 
						 | 
					5382722867 | ||
| 
						 | 
					5e51077347 | ||
| 
						 | 
					6d44069364 | ||
| 
						 | 
					e1e3843ec0 | ||
| 
						 | 
					15905b7022 | ||
| 
						 | 
					1c5ae2f168 | ||
| 
						 | 
					f29e0223a7 | ||
| 
						 | 
					9fbe3cf399 | ||
| 
						 | 
					e4f1da30fc | ||
| 
						 | 
					21deab4bf1 | ||
| 
						 | 
					d7d7dcbcf7 | ||
| 
						 | 
					3f680385a9 | ||
| 
						 | 
					7ecf4bbf1c | ||
| 
						 | 
					ae65985fbc | ||
| 
						 | 
					2a34c3ebb2 | ||
| 
						 | 
					3b46f8c8cf | ||
| 
						 | 
					7c2efb1aef | ||
| 
						 | 
					ea25474529 | ||
| 
						 | 
					33e1d34ce5 | ||
| 
						 | 
					142fb33d81 | ||
| 
						 | 
					ee6c0388da | ||
| 
						 | 
					c171817d6a | ||
| 
						 | 
					71dddee146 | ||
| 
						 | 
					d456ff64f1 | ||
| 
						 | 
					9e78fcccf7 | ||
| 
						 | 
					878cd7e9f0 | ||
| 
						 | 
					5c9721cfac | ||
| 
						 | 
					31502dccc7 | ||
| 
						 | 
					538aa8d908 | ||
| 
						 | 
					00003b2c57 | ||
| 
						 | 
					a2c238d466 | ||
| 
						 | 
					d89f147c54 | ||
| 
						 | 
					53cf1b2013 | ||
| 
						 | 
					564ab331d7 | ||
| 
						 | 
					a690ece164 | ||
| 
						 | 
					b50904c6ff | ||
| 
						 | 
					70aa14ecf8 | ||
| 
						 | 
					c37b92978a | ||
| 
						 | 
					ef39ad7107 | ||
| 
						 | 
					48d3ef9818 | ||
| 
						 | 
					5bf901cdcd | ||
| 
						 | 
					8e99dd306a | ||
| 
						 | 
					07fdc240d7 | ||
| 
						 | 
					023ceaaf91 | ||
| 
						 | 
					9e551a0b2a | ||
| 
						 | 
					7c3f3523ea | ||
| 
						 | 
					40eac07789 | ||
| 
						 | 
					5868fadbf5 | ||
| 
						 | 
					124bcc4bba | ||
| 
						 | 
					e71d6fa983 | ||
| 
						 | 
					7129ad4fac | ||
| 
						 | 
					16923cc86a | ||
| 
						 | 
					3761473967 | ||
| 
						 | 
					34031cae8d | ||
| 
						 | 
					26abb98747 | ||
| 
						 | 
					c2f67b4a77 | ||
| 
						 | 
					4d925a4d62 | ||
| 
						 | 
					4e62054bd1 | ||
| 
						 | 
					fe40d7db32 | ||
| 
						 | 
					c0eeafb5cd | ||
| 
						 | 
					5626b97a19 | ||
| 
						 | 
					0f95e9393d | ||
| 
						 | 
					5de1ffff90 | ||
| 
						 | 
					33698ee448 | ||
| 
						 | 
					5e5d478cf2 | ||
| 
						 | 
					778096d100 | ||
| 
						 | 
					bba163f7b4 | ||
| 
						 | 
					d4f792810e | ||
| 
						 | 
					d14ee59912 | ||
| 
						 | 
					be5d69d5a5 | ||
| 
						 | 
					97c36674e4 | ||
| 
						 | 
					1f1564fad9 | ||
| 
						 | 
					c79e053bea | ||
| 
						 | 
					92e9ed771b | ||
| 
						 | 
					800c6c8ff3 | ||
| 
						 | 
					865627fdad | ||
| 
						 | 
					192537672e | ||
| 
						 | 
					384f9528e7 | ||
| 
						 | 
					5fc76b6426 | ||
| 
						 | 
					4d8a45204c | ||
| 
						 | 
					2de9397db8 | ||
| 
						 | 
					bfc73ed214 | ||
| 
						 | 
					8bf741fd5b | ||
| 
						 | 
					460545a75e | ||
| 
						 | 
					a93b30ec91 | ||
| 
						 | 
					34bac1add9 | ||
| 
						 | 
					f028cb76fc | ||
| 
						 | 
					5a4be5fba1 | ||
| 
						 | 
					23245b78ca | ||
| 
						 | 
					13ac302525 | ||
| 
						 | 
					96a62a3564 | ||
| 
						 | 
					7adf702283 | ||
| 
						 | 
					279c8e014a | ||
| 
						 | 
					b7517cbbd4 | ||
| 
						 | 
					45eac02f4f | ||
| 
						 | 
					a6b7c3afe6 | ||
| 
						 | 
					e99e4f6c58 | ||
| 
						 | 
					bcb97bc406 | ||
| 
						 | 
					ad01406fc1 | ||
| 
						 | 
					15eb08c065 | ||
| 
						 | 
					2340556091 | ||
| 
						 | 
					65c54184e8 | ||
| 
						 | 
					9dcb7c6a12 | ||
| 
						 | 
					0c6faa751a | ||
| 
						 | 
					b465cb34de | ||
| 
						 | 
					21c12a791a | ||
| 
						 | 
					723a0b6d9c | ||
| 
						 | 
					c4ef053958 | ||
| 
						 | 
					055d1f3bb2 | ||
| 
						 | 
					fe27d8920a | ||
| 
						 | 
					df65670d3d | ||
| 
						 | 
					2623d0b343 | ||
| 
						 | 
					c0e0b41d13 | ||
| 
						 | 
					8763bfa3d3 | ||
| 
						 | 
					71180584da | ||
| 
						 | 
					319a89e320 | ||
| 
						 | 
					0673cf8849 | ||
| 
						 | 
					b537899e62 | ||
| 
						 | 
					7b679e60e0 | ||
| 
						 | 
					bb475a6088 | ||
| 
						 | 
					a217c495d1 | ||
| 
						 | 
					bdb86e2b3a | ||
| 
						 | 
					e8700ac44b | ||
| 
						 | 
					d80f6ab695 | ||
| 
						 | 
					381be5a1a1 | ||
| 
						 | 
					214cbac9a6 | ||
| 
						 | 
					906a031172 | ||
| 
						 | 
					236dd6e054 | ||
| 
						 | 
					eb17eb6559 | ||
| 
						 | 
					2746af21f0 | ||
| 
						 | 
					78abb617ce | ||
| 
						 | 
					3c57c0e7f9 | ||
| 
						 | 
					934bbe8bd7 | ||
| 
						 | 
					718a010c0f | ||
| 
						 | 
					a87071b834 | ||
| 
						 | 
					2c598f93ab | ||
| 
						 | 
					0937093851 | ||
| 
						 | 
					b528f0bd14 | ||
| 
						 | 
					7c2c82fc0a | ||
| 
						 | 
					ffe8b16ff3 | ||
| 
						 | 
					ecf7ebad53 | ||
| 
						 | 
					3bf26cd509 | ||
| 
						 | 
					a671d4a8a8 | ||
| 
						 | 
					d9713d0f8c | ||
| 
						 | 
					aeaa33ebd3 | ||
| 
						 | 
					c64de03d27 | ||
| 
						 | 
					2d99304396 | ||
| 
						 | 
					a22dc9537f | ||
| 
						 | 
					6c28f8a0dd | ||
| 
						 | 
					c100168374 | ||
| 
						 | 
					8636d8b3e8 | ||
| 
						 | 
					37b2d648b1 | ||
| 
						 | 
					27b4992f6e | ||
| 
						 | 
					001297ca7a | ||
| 
						 | 
					4f3cbc4bc2 | ||
| 
						 | 
					008e02a406 | ||
| 
						 | 
					e8acfac091 | ||
| 
						 | 
					e1a26b0388 | ||
| 
						 | 
					a6fc47b4f6 | ||
| 
						 | 
					ce7f5121b0 | ||
| 
						 | 
					b38ca837d6 | ||
| 
						 | 
					a2714fb9f7 | ||
| 
						 | 
					8c57d694c5 | ||
| 
						 | 
					0ae521a7dc | ||
| 
						 | 
					79ec3fd2c9 | ||
| 
						 | 
					69b95b3e7a | ||
| 
						 | 
					9d8b9fabbe | ||
| 
						 | 
					875c9fa77c | ||
| 
						 | 
					6bc2d9d4a7 | ||
| 
						 | 
					fe5a1f358d | ||
| 
						 | 
					1972537176 | ||
| 
						 | 
					8f21e9e2fd | ||
| 
						 | 
					089a79002f | ||
| 
						 | 
					ece1dee990 | ||
| 
						 | 
					652f5161a9 | ||
| 
						 | 
					4ba4ea4fcc | ||
| 
						 | 
					3444b50da6 | ||
| 
						 | 
					62b7d96551 | ||
| 
						 | 
					cb71df8a42 | ||
| 
						 | 
					dd54cc972a | ||
| 
						 | 
					6f14c91d30 | ||
| 
						 | 
					6f0dd8dc89 | ||
| 
						 | 
					7d82f954ac | ||
| 
						 | 
					69130a76e4 | ||
| 
						 | 
					9086d32bee | ||
| 
						 | 
					a680310f80 | ||
| 
						 | 
					a07880e1d0 | ||
| 
						 | 
					ae584d54a6 | ||
| 
						 | 
					e36e8f7758 | ||
| 
						 | 
					b47798ef19 | ||
| 
						 | 
					55b1a67637 | ||
| 
						 | 
					d2b9cd2797 | ||
| 
						 | 
					9a95c46578 | ||
| 
						 | 
					a58f72868a | ||
| 
						 | 
					820db87604 | ||
| 
						 | 
					254e61ab01 | ||
| 
						 | 
					ad53965626 | ||
| 
						 | 
					4de9fa33b7 | ||
| 
						 | 
					08e40b611b | ||
| 
						 | 
					8bd2e27653 | ||
| 
						 | 
					d023510f7e | ||
| 
						 | 
					ec5ca0a08f | ||
| 
						 | 
					a46c798e01 | ||
| 
						 | 
					41a3bdf73d | ||
| 
						 | 
					a7b83672ba | ||
| 
						 | 
					c2746c2392 | ||
| 
						 | 
					492e7dab26 | ||
| 
						 | 
					5480e419b6 | ||
| 
						 | 
					251a617ecc | ||
| 
						 | 
					36de389fa4 | ||
| 
						 | 
					f4f052deb4 | ||
| 
						 | 
					3a0fbd45ae | ||
| 
						 | 
					f20c271972 | ||
| 
						 | 
					ad85fa2016 | ||
| 
						 | 
					1a403361c9 | ||
| 
						 | 
					9ad64521d3 | ||
| 
						 | 
					ff76df9ae0 | ||
| 
						 | 
					da1cd55c1d | ||
| 
						 | 
					a65baf5d67 | ||
| 
						 | 
					489cb52976 | ||
| 
						 | 
					591f86e4e9 | ||
| 
						 | 
					9768023d38 | ||
| 
						 | 
					60af92ed2d | ||
| 
						 | 
					4d566071db | ||
| 
						 | 
					aface5ded1 | ||
| 
						 | 
					7f6b71d938 | ||
| 
						 | 
					c8ed71d010 | ||
| 
						 | 
					df6649907d | ||
| 
						 | 
					2fb1c99f56 | ||
| 
						 | 
					fcebda8987 | ||
| 
						 | 
					0185a468bd | ||
| 
						 | 
					c566f2ae28 | ||
| 
						 | 
					29c5ff89ba | ||
| 
						 | 
					ab3e4978b1 | ||
| 
						 | 
					0027f671d2 | ||
| 
						 | 
					06a8ab0ab2 | ||
| 
						 | 
					4352b3fe4a | ||
| 
						 | 
					dd17246086 | ||
| 
						 | 
					d7b0dc91d5 | ||
| 
						 | 
					4598d4d843 | ||
| 
						 | 
					8a731efe0d | ||
| 
						 | 
					59fd7eeeb3 | ||
| 
						 | 
					552e543471 | ||
| 
						 | 
					b1badca062 | ||
| 
						 | 
					9f34edee4f | ||
| 
						 | 
					d257de7882 | ||
| 
						 | 
					4821902fdc | ||
| 
						 | 
					a1f82a7d08 | ||
| 
						 | 
					9009a90ef2 | ||
| 
						 | 
					fc9c90ad99 | ||
| 
						 | 
					9e613488f1 | ||
| 
						 | 
					5e6cb0dd3c | ||
| 
						 | 
					2bb787886d | ||
| 
						 | 
					ed6f8262c6 | ||
| 
						 | 
					64d574cf06 | ||
| 
						 | 
					e0b0ffcb28 | ||
| 
						 | 
					c17225abb1 | ||
| 
						 | 
					19aed0a1e4 | ||
| 
						 | 
					14c1bde958 | ||
| 
						 | 
					eed929b9fe | ||
| 
						 | 
					eda67dd572 | ||
| 
						 | 
					fef2d5207b | ||
| 
						 | 
					87294b41af | ||
| 
						 | 
					99d9c516fc | ||
| 
						 | 
					476c7a77c8 | ||
| 
						 | 
					8dc4b5cf6b | ||
| 
						 | 
					eba6b48daf | ||
| 
						 | 
					1aff46bc1c | ||
| 
						 | 
					e78684886d | ||
| 
						 | 
					7f35925794 | ||
| 
						 | 
					71a59d3e5f | ||
| 
						 | 
					4af46a6045 | ||
| 
						 | 
					d194b39e57 | ||
| 
						 | 
					cd08f66c59 | ||
| 
						 | 
					ef919b9f3d | ||
| 
						 | 
					847b158283 | ||
| 
						 | 
					dd2abd95c9 | ||
| 
						 | 
					b5908d52d7 | ||
| 
						 | 
					bf515042d0 | ||
| 
						 | 
					e94fccc784 | ||
| 
						 | 
					6d45199592 | ||
| 
						 | 
					57b3b329a8 | ||
| 
						 | 
					a0fc268bb9 | ||
| 
						 | 
					631739733f | ||
| 
						 | 
					2d2bd48963 | ||
| 
						 | 
					f6d9bec16c | ||
| 
						 | 
					0f1118e03a | ||
| 
						 | 
					009ac75229 | ||
| 
						 | 
					b64ac8d7f6 | ||
| 
						 | 
					41240fc415 | ||
| 
						 | 
					a18e430056 | ||
| 
						 | 
					1a993a7899 | ||
| 
						 | 
					d3b5220dc3 | ||
| 
						 | 
					4f92c0317e | ||
| 
						 | 
					a098565c37 | ||
| 
						 | 
					0d5fe5d91e | ||
| 
						 | 
					5c66f3b90c | ||
| 
						 | 
					63d22b9b33 | ||
| 
						 | 
					b4678b74ab | ||
| 
						 | 
					a82ed1e9dd | ||
| 
						 | 
					d2ffbfb80b | ||
| 
						 | 
					5d61782a6c | ||
| 
						 | 
					1b8c9fdaa9 | ||
| 
						 | 
					90328ae79b | ||
| 
						 | 
					9cdcbbccbf | ||
| 
						 | 
					b820a98c6c | ||
| 
						 | 
					8e749c472a | ||
| 
						 | 
					70a5077291 | ||
| 
						 | 
					8a5d8cc9b9 | ||
| 
						 | 
					0a0a16f969 | ||
| 
						 | 
					73d5fbd085 | ||
| 
						 | 
					9da3d25292 | ||
| 
						 | 
					0385a444c2 | ||
| 
						 | 
					7d856b030b | ||
| 
						 | 
					8c0441b91a | ||
| 
						 | 
					baef55ae1b | ||
| 
						 | 
					80e6943d2e | ||
| 
						 | 
					2ffdd56301 | ||
| 
						 | 
					53635da552 | ||
| 
						 | 
					5e7fb88762 | ||
| 
						 | 
					d89727725b | ||
| 
						 | 
					8cd7e3c924 | ||
| 
						 | 
					0baf2c5861 | ||
| 
						 | 
					b0548f9a56 | ||
| 
						 | 
					2d190cfb19 | ||
| 
						 | 
					3b5858b114 | ||
| 
						 | 
					112157ade0 | ||
| 
						 | 
					8cd30ae86b | ||
| 
						 | 
					9b6b288e73 | ||
| 
						 | 
					1757e5519d | ||
| 
						 | 
					a21fa666fd | ||
| 
						 | 
					df9a57c379 | ||
| 
						 | 
					5f31efd33e | ||
| 
						 | 
					2a9f245b39 | ||
| 
						 | 
					761586cc3c | ||
| 
						 | 
					6731b7947b | ||
| 
						 | 
					ddc8bd1139 | ||
| 
						 | 
					6ae9bbdb31 | ||
| 
						 | 
					6c950c9569 | ||
| 
						 | 
					3ce3ffca05 | ||
| 
						 | 
					d3ccc43d68 | ||
| 
						 | 
					4c96440686 | ||
| 
						 | 
					8d8d76364b | ||
| 
						 | 
					82af98c205 | ||
| 
						 | 
					fdbe8c2395 | ||
| 
						 | 
					59715b1e02 | ||
| 
						 | 
					f2ec530065 | ||
| 
						 | 
					ac89cb46f5 | ||
| 
						 | 
					abfa995a32 | ||
| 
						 | 
					51cfbef887 | ||
| 
						 | 
					7171ed1508 | ||
| 
						 | 
					6727f3e6a4 | ||
| 
						 | 
					81006c758a | ||
| 
						 | 
					01025c4024 | ||
| 
						 | 
					517277132e | ||
| 
						 | 
					dc5c92ee2d | ||
| 
						 | 
					57e142e160 | ||
| 
						 | 
					c563afed59 | ||
| 
						 | 
					556cf87f0e | ||
| 
						 | 
					e0a00cfe98 | ||
| 
						 | 
					d60774a7b8 | ||
| 
						 | 
					e3d40b75cb | ||
| 
						 | 
					eb4479e940 | ||
| 
						 | 
					7e6d0a5c64 | ||
| 
						 | 
					88dd4165cb | ||
| 
						 | 
					e96118c574 | ||
| 
						 | 
					39c4e5fe55 | ||
| 
						 | 
					46141dc114 | ||
| 
						 | 
					e3a25f2425 | ||
| 
						 | 
					64289c16f3 | ||
| 
						 | 
					6b05ddb576 | ||
| 
						 | 
					88ee252fce | ||
| 
						 | 
					2add7291ab | ||
| 
						 | 
					501be029c6 | ||
| 
						 | 
					d86652dee1 | ||
| 
						 | 
					272ca613ee | ||
| 
						 | 
					74af811a3b | ||
| 
						 | 
					fc72b67090 | ||
| 
						 | 
					e33f76d710 | ||
| 
						 | 
					1c3d594947 | ||
| 
						 | 
					64d9b27310 | ||
| 
						 | 
					f3f3593cfe | ||
| 
						 | 
					e5e8e3ce7c | ||
| 
						 | 
					a7fd7ba72c | ||
| 
						 | 
					020f090f4a | ||
| 
						 | 
					5e3231d59b | ||
| 
						 | 
					b522bc015d | ||
| 
						 | 
					6e64fd7fd7 | ||
| 
						 | 
					8b44f5cdbc | ||
| 
						 | 
					f9b7d955aa | ||
| 
						 | 
					6ea2a2fc51 | ||
| 
						 | 
					760c8d7200 | ||
| 
						 | 
					32ad28c3dc | ||
| 
						 | 
					6886e9fd5b | ||
| 
						 | 
					f20130d3db | ||
| 
						 | 
					df9cc881f1 | ||
| 
						 | 
					4044988afa | ||
| 
						 | 
					d3360e81b9 | ||
| 
						 | 
					df070b7d78 | ||
| 
						 | 
					83dd98faf3 | ||
| 
						 | 
					37f89f560f | ||
| 
						 | 
					918ed0d6d0 | ||
| 
						 | 
					a3c9edde78 | ||
| 
						 | 
					cac0a4cd16 | ||
| 
						 | 
					581b6e03d5 | ||
| 
						 | 
					801cc584e5 | ||
| 
						 | 
					b82ff8617e | ||
| 
						 | 
					c87016c1af | ||
| 
						 | 
					463048e017 | ||
| 
						 | 
					79aee1d312 | ||
| 
						 | 
					eb038e91dd | ||
| 
						 | 
					308c22f922 | ||
| 
						 | 
					457e59e61c | ||
| 
						 | 
					a964ccbd10 | ||
| 
						 | 
					0af532f4f1 | ||
| 
						 | 
					4743eb1d3b | ||
| 
						 | 
					23aafe1cfe | ||
| 
						 | 
					3f2499feac | ||
| 
						 | 
					fb97649151 | ||
| 
						 | 
					b33b645ef0 | ||
| 
						 | 
					9318f182b0 | ||
| 
						 | 
					facbb7f28f | ||
| 
						 | 
					4de45ce170 | ||
| 
						 | 
					96d57bd263 | ||
| 
						 | 
					bb4587fe05 | ||
| 
						 | 
					19c83f02aa | ||
| 
						 | 
					0e1fcbfe9c | ||
| 
						 | 
					eda882433a | ||
| 
						 | 
					e6847605cc | ||
| 
						 | 
					5bdffdb368 | ||
| 
						 | 
					0ad52b18b8 | ||
| 
						 | 
					77f44574c0 | ||
| 
						 | 
					ca06a2311d | ||
| 
						 | 
					b8d9af65e2 | ||
| 
						 | 
					bc2b4876b6 | ||
| 
						 | 
					23b70ca0be | ||
| 
						 | 
					44d776a76f | ||
| 
						 | 
					f03c00b2c1 | ||
| 
						 | 
					7f60ba9888 | ||
| 
						 | 
					31569646b0 | ||
| 
						 | 
					3fc37d6362 | ||
| 
						 | 
					6d28072167 | ||
| 
						 | 
					f124fbd6aa | ||
| 
						 | 
					88a4a51956 | ||
| 
						 | 
					4306ea4181 | ||
| 
						 | 
					e19140462d | ||
| 
						 | 
					20cc8a6d6c | ||
| 
						 | 
					a9d7a42c65 | ||
| 
						 | 
					f51e6d81b1 | ||
| 
						 | 
					f119d082cf | ||
| 
						 | 
					ecfaa9ad5c | ||
| 
						 | 
					f32d0266ee | ||
| 
						 | 
					7393a61305 | ||
| 
						 | 
					2b0efd1f93 | ||
| 
						 | 
					b615a3b088 | ||
| 
						 | 
					85403e975f | ||
| 
						 | 
					615ad918ca | ||
| 
						 | 
					b886f3a04b | ||
| 
						 | 
					588a47897a | ||
| 
						 | 
					2869d590e6 | ||
| 
						 | 
					6b14bce25e | ||
| 
						 | 
					5aa346327f | ||
| 
						 | 
					fcf8516f0d | ||
| 
						 | 
					2a340d4d83 | ||
| 
						 | 
					508d7a37e3 | ||
| 
						 | 
					08fece39d8 | ||
| 
						 | 
					857a0b1006 | ||
| 
						 | 
					239d59c864 | ||
| 
						 | 
					7297053dd6 | ||
| 
						 | 
					bd872f624a | ||
| 
						 | 
					86acb14f05 | ||
| 
						 | 
					9825f349ac | ||
| 
						 | 
					19fd562c24 | ||
| 
						 | 
					b6d939a9ff | ||
| 
						 | 
					1619edb8a1 | ||
| 
						 | 
					782821aeb2 | ||
| 
						 | 
					51498958fa | ||
| 
						 | 
					51edb74474 | ||
| 
						 | 
					d5ab2a7557 | ||
| 
						 | 
					ee3525cfb2 | ||
| 
						 | 
					e9bd0858e2 | ||
| 
						 | 
					f46d881866 | ||
| 
						 | 
					ee5e718f83 | ||
| 
						 | 
					e25083aea4 | ||
| 
						 | 
					e74f0ca6f8 | ||
| 
						 | 
					52b0fa9a54 | ||
| 
						 | 
					105c007f03 | ||
| 
						 | 
					0a3d5fd5d4 | ||
| 
						 | 
					ae3c02d4b2 | ||
| 
						 | 
					9e17d07a17 | ||
| 
						 | 
					dcfab4e011 | ||
| 
						 | 
					0b78f9361d | ||
| 
						 | 
					0c4e9dc813 | ||
| 
						 | 
					d894cae073 | ||
| 
						 | 
					84f553a911 | ||
| 
						 | 
					05580deaa9 | ||
| 
						 | 
					aac83bbb91 | ||
| 
						 | 
					249f1f48a6 | ||
| 
						 | 
					bfb92fe667 | ||
| 
						 | 
					640dc43bbe | ||
| 
						 | 
					8859d915b0 | ||
| 
						 | 
					82fdb37c6b | ||
| 
						 | 
					49c18dab63 | ||
| 
						 | 
					f5b8a22bde | ||
| 
						 | 
					b6b0f9c47d | ||
| 
						 | 
					a2a2fa2311 | ||
| 
						 | 
					34690e3e65 | ||
| 
						 | 
					54f58257f9 | ||
| 
						 | 
					58b6c4668f | ||
| 
						 | 
					d0e7eb8409 | ||
| 
						 | 
					77a7a8f30e | ||
| 
						 | 
					f76738e02b | ||
| 
						 | 
					ab147df2f1 | ||
| 
						 | 
					5444ccc857 | ||
| 
						 | 
					fc89d62f1a | ||
| 
						 | 
					94467273c5 | ||
| 
						 | 
					835de64bea | ||
| 
						 | 
					113da3437b | ||
| 
						 | 
					b0b6d01357 | ||
| 
						 | 
					6cc24dc763 | ||
| 
						 | 
					0cb3105cea | ||
| 
						 | 
					00502a4689 | ||
| 
						 | 
					ebb7242b71 | ||
| 
						 | 
					0fbb96c4ac | ||
| 
						 | 
					e942ffed71 | ||
| 
						 | 
					319270bf2b | ||
| 
						 | 
					8b69de0d54 | ||
| 
						 | 
					f929513310 | ||
| 
						 | 
					0d25b82087 | ||
| 
						 | 
					c75857b1ea | ||
| 
						 | 
					6d353869ef | ||
| 
						 | 
					6ba7249a75 | ||
| 
						 | 
					2ee543e2a4 | ||
| 
						 | 
					871dfa9a67 | ||
| 
						 | 
					17fe2d5863 | ||
| 
						 | 
					d5b62a2126 | ||
| 
						 | 
					a4a833f15f | ||
| 
						 | 
					a4fe077a23 | ||
| 
						 | 
					bfa77361b7 | ||
| 
						 | 
					3dff529920 | ||
| 
						 | 
					3681150010 | ||
| 
						 | 
					f0b4fcbdf0 | ||
| 
						 | 
					5c7e8c5381 | ||
| 
						 | 
					a144fa449b | ||
| 
						 | 
					4f9ceb0a80 | ||
| 
						 | 
					12338fc0b4 | ||
| 
						 | 
					6d2cc6e87d | ||
| 
						 | 
					52598e5c5c | ||
| 
						 | 
					903d810edc | ||
| 
						 | 
					475b169952 | ||
| 
						 | 
					ba1f66367b | ||
| 
						 | 
					b27f560b33 | ||
| 
						 | 
					59edca8fca | ||
| 
						 | 
					147a90302d | ||
| 
						 | 
					b4b5aedc82 | ||
| 
						 | 
					81293c9368 | ||
| 
						 | 
					7c82b4c370 | ||
| 
						 | 
					045e09f029 | ||
| 
						 | 
					cbd59f84fd | ||
| 
						 | 
					086b206139 | ||
| 
						 | 
					d56e8d79ec | ||
| 
						 | 
					d61459e912 | ||
| 
						 | 
					33880f3e86 | ||
| 
						 | 
					6186881615 | ||
| 
						 | 
					20a3396128 | ||
| 
						 | 
					e5ee98a6aa | ||
| 
						 | 
					8985f1dba6 | ||
| 
						 | 
					5e09884e99 | ||
| 
						 | 
					0cb0a355bb | ||
| 
						 | 
					26445851cc | ||
| 
						 | 
					810691b5df | ||
| 
						 | 
					5d18d02a9c | ||
| 
						 | 
					65421639f1 | ||
| 
						 | 
					a474e9e5d1 | ||
| 
						 | 
					7b9358a4f0 | ||
| 
						 | 
					ff684cad44 | ||
| 
						 | 
					0b4f6dd29b | ||
| 
						 | 
					b4f512f667 | ||
| 
						 | 
					7dda9974f1 | ||
| 
						 | 
					95791254a9 | ||
| 
						 | 
					e0253a4ea8 | ||
| 
						 | 
					cfc2dba275 | ||
| 
						 | 
					867e7bf665 | ||
| 
						 | 
					7c448aed9f | ||
| 
						 | 
					8a388a0d8b | ||
| 
						 | 
					62a2e9de37 | ||
| 
						 | 
					1184274246 | ||
| 
						 | 
					51d66199b1 | ||
| 
						 | 
					e7e988f97a | ||
| 
						 | 
					a17211dc96 | ||
| 
						 | 
					2c1769575c | ||
| 
						 | 
					5d3af1a932 | ||
| 
						 | 
					f2e0ffb2b2 | ||
| 
						 | 
					2e32b748b6 | ||
| 
						 | 
					512b5204bc | ||
| 
						 | 
					44bef2d6d9 | ||
| 
						 | 
					7e2257b224 | ||
| 
						 | 
					0b04ac79d0 | ||
| 
						 | 
					7b02ad3c00 | ||
| 
						 | 
					9b507f06c4 | ||
| 
						 | 
					6ce92e1669 | ||
| 
						 | 
					d443304829 | ||
| 
						 | 
					c84f24fe20 | ||
| 
						 | 
					9175b7a4c1 | ||
| 
						 | 
					1c011fbd7b | ||
| 
						 | 
					56531afd1a | ||
| 
						 | 
					b6e294c137 | ||
| 
						 | 
					0f9936a542 | ||
| 
						 | 
					9855f60f1f | ||
| 
						 | 
					07adf67dfc | ||
| 
						 | 
					125b03adfc | ||
| 
						 | 
					528b65ae04 | ||
| 
						 | 
					55fda94b6a | ||
| 
						 | 
					67ab7a04ad | ||
| 
						 | 
					94797a2d2b | ||
| 
						 | 
					1bd40a0cff | ||
| 
						 | 
					102003a52b | ||
| 
						 | 
					6777ae046e | ||
| 
						 | 
					a32f55a920 | ||
| 
						 | 
					f3cc05e651 | ||
| 
						 | 
					52a53791c8 | ||
| 
						 | 
					e181e340e4 | ||
| 
						 | 
					2cf2cc46f2 | ||
| 
						 | 
					11bfde3dc1 | ||
| 
						 | 
					dd6d4c67ed | ||
| 
						 | 
					f13b9d2764 | ||
| 
						 | 
					50ed9b327f | ||
| 
						 | 
					49f101308f | ||
| 
						 | 
					2154033879 | ||
| 
						 | 
					ba1a540add | ||
| 
						 | 
					ea23e604f9 | ||
| 
						 | 
					7cf9c15984 | ||
| 
						 | 
					a3f43f8ea3 | ||
| 
						 | 
					3a933e6f2b | ||
| 
						 | 
					9a15298e63 | ||
| 
						 | 
					7367457500 | ||
| 
						 | 
					831aec3af9 | ||
| 
						 | 
					5b67b182c3 | ||
| 
						 | 
					5fc0a298a5 | ||
| 
						 | 
					69e3afc770 | ||
| 
						 | 
					45d431f24e | ||
| 
						 | 
					6a3a27026d | ||
| 
						 | 
					0310637b07 | ||
| 
						 | 
					9662d8a2ab | ||
| 
						 | 
					e81d8527c4 | ||
| 
						 | 
					02a8bfb5ae | ||
| 
						 | 
					845e95bd07 | ||
| 
						 | 
					5eea8fda26 | ||
| 
						 | 
					b9133484f0 | ||
| 
						 | 
					ef4beaceb0 | ||
| 
						 | 
					68dff22b77 | ||
| 
						 | 
					0d0ae31d52 | ||
| 
						 | 
					bdcd889df4 | ||
| 
						 | 
					ef57259386 | ||
| 
						 | 
					416ca05be4 | ||
| 
						 | 
					479b17a8c2 | ||
| 
						 | 
					624fd87751 | ||
| 
						 | 
					727df8dd94 | ||
| 
						 | 
					8ae9fc10eb | ||
| 
						 | 
					1ff6502ea9 | ||
| 
						 | 
					594165da6a | ||
| 
						 | 
					c03c72c7d0 | ||
| 
						 | 
					78115e0504 | ||
| 
						 | 
					cb9ed5add0 | ||
| 
						 | 
					4bb416945b | ||
| 
						 | 
					1375a19a46 | ||
| 
						 | 
					125b50b33e | ||
| 
						 | 
					65d25c6f64 | ||
| 
						 | 
					3879fa5df2 | ||
| 
						 | 
					df1cd7e07f | ||
| 
						 | 
					2a9897057e | ||
| 
						 | 
					ea5d657e31 | ||
| 
						 | 
					a36a07ae6f | ||
| 
						 | 
					acaa220a70 | ||
| 
						 | 
					3ffd1e3b41 | ||
| 
						 | 
					14cedccaf3 | ||
| 
						 | 
					041d8399ba | ||
| 
						 | 
					32171508e1 | ||
| 
						 | 
					11c15c47d1 | ||
| 
						 | 
					853fc6678b | ||
| 
						 | 
					d6db49e621 | ||
| 
						 | 
					405a98c8f3 | ||
| 
						 | 
					cb296ef2cd | ||
| 
						 | 
					bebd9e7a54 | ||
| 
						 | 
					ae0bd608a9 | ||
| 
						 | 
					31d445c6a1 | ||
| 
						 | 
					f7f2c1730d | ||
| 
						 | 
					2782c369c9 | ||
| 
						 | 
					659db611ea | ||
| 
						 | 
					9e5f7be1c5 | ||
| 
						 | 
					bd4f7ff3aa | ||
| 
						 | 
					155aa3ba5c | ||
| 
						 | 
					31cd1258f8 | ||
| 
						 | 
					18e3b424d6 | ||
| 
						 | 
					d403688a0c | ||
| 
						 | 
					17bf0c0623 | ||
| 
						 | 
					b296486892 | ||
| 
						 | 
					64dccb192c | ||
| 
						 | 
					c15a27709d | ||
| 
						 | 
					33de9f9337 | ||
| 
						 | 
					aa0ebd45b9 | ||
| 
						 | 
					2583632883 | ||
| 
						 | 
					2472d531f5 | ||
| 
						 | 
					fb43fb9af7 | ||
| 
						 | 
					56a640a5c6 | ||
| 
						 | 
					ffa702935c | ||
| 
						 | 
					8482203c3b | ||
| 
						 | 
					eb06eb7266 | ||
| 
						 | 
					ea64f505af | ||
| 
						 | 
					748411c8db | ||
| 
						 | 
					90aad8ed9a | ||
| 
						 | 
					6cc2da03e0 | ||
| 
						 | 
					e1b94d6a28 | ||
| 
						 | 
					1abd2e7d7e | ||
| 
						 | 
					8352f18b3a | ||
| 
						 | 
					9ff0b09d4d | ||
| 
						 | 
					2e08825da8 | ||
| 
						 | 
					cf81f641bd | ||
| 
						 | 
					07891edd16 | ||
| 
						 | 
					6ea777d584 | ||
| 
						 | 
					040ecb2532 | ||
| 
						 | 
					f5f8fa2471 | ||
| 
						 | 
					c728116788 | ||
| 
						 | 
					6438f80526 | ||
| 
						 | 
					0b43cf4b1c | ||
| 
						 | 
					8677f78a56 | ||
| 
						 | 
					54606b05c0 | ||
| 
						 | 
					6ad126cf64 | ||
| 
						 | 
					654b84f4f2 | ||
| 
						 | 
					9ddf0c6e76 | ||
| 
						 | 
					7e3984e341 | ||
| 
						 | 
					af77657a86 | ||
| 
						 | 
					eb03afef41 | ||
| 
						 | 
					1da98c8a82 | ||
| 
						 | 
					a73c65eae9 | ||
| 
						 | 
					5dc14666cc | ||
| 
						 | 
					dd219ad295 | ||
| 
						 | 
					4e871e02e6 | ||
| 
						 | 
					cbc1030043 | ||
| 
						 | 
					061c6e822d | ||
| 
						 | 
					17d259dc52 | ||
| 
						 | 
					0a319b31c8 | ||
| 
						 | 
					8b1e34d800 | ||
| 
						 | 
					e548a05cf0 | ||
| 
						 | 
					089b089fce | ||
| 
						 | 
					aa3e8c9986 | ||
| 
						 | 
					497176d9d1 | ||
| 
						 | 
					4e52369542 | ||
| 
						 | 
					ba37178ebb | ||
| 
						 | 
					343de424ab | ||
| 
						 | 
					2d790f3c4d | ||
| 
						 | 
					651b2e140b | ||
| 
						 | 
					c859fa4c38 | ||
| 
						 | 
					26e149a357 | ||
| 
						 | 
					2ed765d204 | ||
| 
						 | 
					8e091d712f | ||
| 
						 | 
					812d9eb0e8 | ||
| 
						 | 
					ed43774129 | ||
| 
						 | 
					945202652a | ||
| 
						 | 
					6954fdec0a | ||
| 
						 | 
					148ad7a3d1 | ||
| 
						 | 
					b209030f45 | ||
| 
						 | 
					425dbd5604 | ||
| 
						 | 
					4cf14ef5ac | ||
| 
						 | 
					f429065ab4 | ||
| 
						 | 
					9d35575051 | ||
| 
						 | 
					b5ad057f06 | ||
| 
						 | 
					ab2af1669c | ||
| 
						 | 
					11615685b7 | ||
| 
						 | 
					b017cb7b54 | ||
| 
						 | 
					960bea96b4 | ||
| 
						 | 
					979cfc9af0 | ||
| 
						 | 
					ed8202891f | ||
| 
						 | 
					6f5a368c86 | ||
| 
						 | 
					c6c615308c | ||
| 
						 | 
					105dfd96c1 | ||
| 
						 | 
					65c51124f8 | ||
| 
						 | 
					6309af9db8 | ||
| 
						 | 
					5360ec6ec3 | ||
| 
						 | 
					7c7cfc8c39 | ||
| 
						 | 
					f3207649ff | ||
| 
						 | 
					6086db3b0b | ||
| 
						 | 
					fd4c91301e | ||
| 
						 | 
					581d7144d6 | ||
| 
						 | 
					a458c7056d | ||
| 
						 | 
					7b8822f664 | ||
| 
						 | 
					c8cd4e2d01 | ||
| 
						 | 
					abe6b05c5c | ||
| 
						 | 
					1b6b74c67b | ||
| 
						 | 
					df5cbaaea8 | ||
| 
						 | 
					9a14e90642 | ||
| 
						 | 
					fb337f57b8 | ||
| 
						 | 
					9f09083247 | ||
| 
						 | 
					74e55720d7 | ||
| 
						 | 
					e30e4fe447 | ||
| 
						 | 
					5d38f4bb77 | ||
| 
						 | 
					dbef39a7a6 | ||
| 
						 | 
					f66b196046 | ||
| 
						 | 
					bb59cb204d | ||
| 
						 | 
					731bcc7e93 | ||
| 
						 | 
					b3dbb19afc | ||
| 
						 | 
					b4e1e32d20 | ||
| 
						 | 
					c9cceb9e2d | ||
| 
						 | 
					fe13fd899c | ||
| 
						 | 
					81b2999dc4 | ||
| 
						 | 
					598ece677d | ||
| 
						 | 
					2142fc8876 | ||
| 
						 | 
					67d96a63f1 | ||
| 
						 | 
					efc46c17b2 | ||
| 
						 | 
					acae5616f8 | ||
| 
						 | 
					672320f38b | ||
| 
						 | 
					f3cd3bb63f | ||
| 
						 | 
					ea98435acd | ||
| 
						 | 
					d545f8ddda | ||
| 
						 | 
					d4685e5f95 | ||
| 
						 | 
					e7907a4664 | ||
| 
						 | 
					60862ffc3e | ||
| 
						 | 
					154ee06d70 | ||
| 
						 | 
					22d87c7c9d | ||
| 
						 | 
					e8f8b41f8b | ||
| 
						 | 
					079f90766f | ||
| 
						 | 
					acc7f3dfe5 | ||
| 
						 | 
					234d8989d7 | ||
| 
						 | 
					acb6aeffd1 | ||
| 
						 | 
					b8135557e5 | ||
| 
						 | 
					d54772815b | ||
| 
						 | 
					5f7f8a31e9 | ||
| 
						 | 
					56798131b3 | ||
| 
						 | 
					3b932cfa7b | ||
| 
						 | 
					2d27100b5d | ||
| 
						 | 
					5e1bd0d679 | ||
| 
						 | 
					d954cf01f3 | ||
| 
						 | 
					e655da45e2 | ||
| 
						 | 
					c7246b3a84 | ||
| 
						 | 
					bef6bb7004 | ||
| 
						 | 
					ef8c567ab6 | ||
| 
						 | 
					0fd001f229 | ||
| 
						 | 
					55ce0e34c9 | ||
| 
						 | 
					70bf1a48d0 | ||
| 
						 | 
					7b4e8324a9 | ||
| 
						 | 
					3913bf68c6 | ||
| 
						 | 
					ea48115190 | ||
| 
						 | 
					9fdd4d0fba | ||
| 
						 | 
					bec97982a6 | ||
| 
						 | 
					3a4990e3d4 | ||
| 
						 | 
					995ddf6d98 | ||
| 
						 | 
					1997a607ba | ||
| 
						 | 
					3d406c2d07 | ||
| 
						 | 
					591331b70c | ||
| 
						 | 
					4273f2db34 | ||
| 
						 | 
					cb913a9adc | ||
| 
						 | 
					7c5898ddf6 | ||
| 
						 | 
					a07e5d7833 | ||
| 
						 | 
					9766f61cf8 | ||
| 
						 | 
					58657e53bf | ||
| 
						 | 
					391c92a6c6 | ||
| 
						 | 
					65480ebe96 | ||
| 
						 | 
					5e5fe434e2 | ||
| 
						 | 
					b628c9b027 | ||
| 
						 | 
					928e418f3f | ||
| 
						 | 
					e5089dc126 | ||
| 
						 | 
					6cff0375fb | ||
| 
						 | 
					afa8a1f298 | ||
| 
						 | 
					8c3462079b | ||
| 
						 | 
					f1eeb08d90 | ||
| 
						 | 
					cad250f02a | ||
| 
						 | 
					13e60a6048 | ||
| 
						 | 
					e115f5f2f4 | ||
| 
						 | 
					348bd00fa3 | ||
| 
						 | 
					2417517aee | ||
| 
						 | 
					05880981f8 | ||
| 
						 | 
					d4f8b93fe3 | ||
| 
						 | 
					7f64fa7037 | ||
| 
						 | 
					eca2be1a2e | ||
| 
						 | 
					3d03a5b319 | ||
| 
						 | 
					59385fc08d | ||
| 
						 | 
					fcabba1087 | ||
| 
						 | 
					2ac24d62a0 | ||
| 
						 | 
					f0b9c21169 | ||
| 
						 | 
					46e46e60a6 | ||
| 
						 | 
					606290e185 | ||
| 
						 | 
					845b57e931 | ||
| 
						 | 
					e7ca94bab1 | ||
| 
						 | 
					a7bb4ee50c | ||
| 
						 | 
					339f85741f | ||
| 
						 | 
					63374ee876 | ||
| 
						 | 
					e77ede91b9 | ||
| 
						 | 
					30d7651322 | ||
| 
						 | 
					f101d70523 | ||
| 
						 | 
					5cf84980e8 | ||
| 
						 | 
					3035eb4a54 | ||
| 
						 | 
					b4710edc18 | ||
| 
						 | 
					3e2a6492f4 | ||
| 
						 | 
					e11b1bb2ec | ||
| 
						 | 
					57318cc55d | ||
| 
						 | 
					8d3d93e537 | ||
| 
						 | 
					df5edb67f0 | ||
| 
						 | 
					12b40f2bbe | ||
| 
						 | 
					8660db3bb3 | ||
| 
						 | 
					649099a841 | ||
| 
						 | 
					b4f91a9bbd | ||
| 
						 | 
					f72ce39c13 | ||
| 
						 | 
					f5420f1f07 | ||
| 
						 | 
					2cf7c45ac5 | ||
| 
						 | 
					4ceb79afa3 | ||
| 
						 | 
					84671e5972 | ||
| 
						 | 
					9c84530593 | ||
| 
						 | 
					363af040d6 | ||
| 
						 | 
					2ea30af4c4 | ||
| 
						 | 
					6ccef6c1d0 | ||
| 
						 | 
					2a9624d404 | ||
| 
						 | 
					a1f404d548 | ||
| 
						 | 
					c95a197028 | ||
| 
						 | 
					82c62091aa | ||
| 
						 | 
					203233fbaf | ||
| 
						 | 
					5bc6b1de3a | ||
| 
						 | 
					7445934371 | ||
| 
						 | 
					521596bc12 | ||
| 
						 | 
					00d85947b0 | ||
| 
						 | 
					a62eba5c37 | ||
| 
						 | 
					102d854743 | ||
| 
						 | 
					953de2fb1c | ||
| 
						 | 
					c241a715a4 | ||
| 
						 | 
					3a51c58b82 | ||
| 
						 | 
					0ac4a63e91 | ||
| 
						 | 
					e981e2f69f | ||
| 
						 | 
					296392b094 | ||
| 
						 | 
					e1e409d89e | ||
| 
						 | 
					7fb4d17913 | ||
| 
						 | 
					469c334b95 | ||
| 
						 | 
					ddcb13c7c3 | ||
| 
						 | 
					c4582b085f | ||
| 
						 | 
					45097ccb8f | ||
| 
						 | 
					663e22ac29 | ||
| 
						 | 
					0403c2c274 | ||
| 
						 | 
					48fdfffc42 | ||
| 
						 | 
					6037edb621 | ||
| 
						 | 
					ce8a82383d | ||
| 
						 | 
					e1390615b7 | ||
| 
						 | 
					860afb8738 | ||
| 
						 | 
					e509d95af9 | ||
| 
						 | 
					4cefcf6ada | ||
| 
						 | 
					affbce1957 | ||
| 
						 | 
					5d97541b8f | ||
| 
						 | 
					8ed5c75c6a | ||
| 
						 | 
					a1adfa2e93 | ||
| 
						 | 
					1999000322 | ||
| 
						 | 
					677fe7e2a5 | ||
| 
						 | 
					8704cae182 | ||
| 
						 | 
					7dde869eba | ||
| 
						 | 
					cb9a3c36e6 | ||
| 
						 | 
					d131a833ab | ||
| 
						 | 
					be1398137a | ||
| 
						 | 
					183e39f1cd | ||
| 
						 | 
					a6fc191df2 | ||
| 
						 | 
					87981fb80c | ||
| 
						 | 
					96ab48396c | ||
| 
						 | 
					40ec6584c5 | ||
| 
						 | 
					d811cb8e04 | ||
| 
						 | 
					d3ad832ded | ||
| 
						 | 
					b0cc1a913e | ||
| 
						 | 
					b0faebc5e6 | ||
| 
						 | 
					c45ffaec0f | ||
| 
						 | 
					78c91d0733 | ||
| 
						 | 
					3f1e5702a2 | ||
| 
						 | 
					6bed331971 | ||
| 
						 | 
					793737d69d | ||
| 
						 | 
					4e8f2b130e | ||
| 
						 | 
					ac6fe634dc | ||
| 
						 | 
					1e1616ceb0 | ||
| 
						 | 
					3046362ff4 | ||
| 
						 | 
					f2956c322e | ||
| 
						 | 
					754c22f8a6 | ||
| 
						 | 
					16685a8e0c | ||
| 
						 | 
					e2692aa9e9 | ||
| 
						 | 
					9b0938e0d6 | ||
| 
						 | 
					c4f69b466a | ||
| 
						 | 
					3acbf6efee | 
@@ -1,49 +0,0 @@
 | 
				
			|||||||
### 使用版本(未按照模板填写直接删除)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- jdk版本(带上尾号): 例如 1.8.0_202
 | 
					 | 
				
			||||||
- 框架版本(项目启动时输出的版本号): 例如 4.4.0
 | 
					 | 
				
			||||||
- 其他依赖版本(你觉得有必要的):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 问题前提
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 功能不好用 不会用 是否已经看过项目文档
 | 
					 | 
				
			||||||
> 项目运行报错 是否已经拿着报错信息去百度 常见报错百度百度足以
 | 
					 | 
				
			||||||
> 是否搜索过其他issue 一些已经解决的问题 会在issue内留下解决方法
 | 
					 | 
				
			||||||
> 无法线上解决或者与框架无关的问题的欢迎加VIP群跟作者一对一谈
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 异常模块
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 此报错都涉及到那些系统模块
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
例如 ruoyi-system ruoyi-auth 等等
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 问题描述
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 越详细越容易直击问题所在
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
已知: XXX功能不好用 或 XXX数据不正常 等等
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 希望结果
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 想知道你觉得怎么样是正常或者合理的
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
希望功能可以有XXX结果 或者 XXX现象
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 重现步骤
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 作者并不知道这个问题是如何出现的
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- 1
 | 
					 | 
				
			||||||
- 2
 | 
					 | 
				
			||||||
- 3
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
### 相关代码与报错信息(请勿发混乱格式)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
> 代码可按照如下形式提供或者截图均可 越详细越好
 | 
					 | 
				
			||||||
> 大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
```java
 | 
					 | 
				
			||||||
public class XXX {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
```
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
name: Bug 反馈
 | 
					name: Bug 反馈
 | 
				
			||||||
description: 当你中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲。
 | 
					description: 当你使用过程中发现了一个 Bug,导致应用崩溃或抛出异常,或者有一个组件存在问题,或者某些地方看起来不对劲,请在这里反馈。
 | 
				
			||||||
title: "[Bug]: "
 | 
					title: "[Bug]: "
 | 
				
			||||||
labels: ["bug"]
 | 
					labels: ["bug"]
 | 
				
			||||||
body:
 | 
					body:
 | 
				
			||||||
@@ -9,14 +9,15 @@ body:
 | 
				
			|||||||
      label: 版本
 | 
					      label: 版本
 | 
				
			||||||
      description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)?
 | 
					      description: 你当前正在使用我们软件的哪个版本(pom文件内的版本号)?
 | 
				
			||||||
      value: |
 | 
					      value: |
 | 
				
			||||||
        jdk版本(带上尾号): 例如 1.8.0
 | 
					        注意: 未填写版本号不予处理直接关闭或删除
 | 
				
			||||||
        框架版本(项目启动时输出的版本号): 例如 4.4.0
 | 
					        jdk版本(带上尾号):
 | 
				
			||||||
 | 
					        框架版本(项目启动时输出的版本号):
 | 
				
			||||||
        其他依赖版本(你觉得有必要的):
 | 
					        其他依赖版本(你觉得有必要的):
 | 
				
			||||||
    validations:
 | 
					    validations:
 | 
				
			||||||
      required: true
 | 
					      required: true
 | 
				
			||||||
  - type: checkboxes
 | 
					  - type: checkboxes
 | 
				
			||||||
    attributes:
 | 
					    attributes:
 | 
				
			||||||
      label: 功能不好用不会用是否已经看过项目文档?
 | 
					      label: 功能不好用不会用是否已经看过项目文档?
 | 
				
			||||||
      options:
 | 
					      options:
 | 
				
			||||||
        - label: https://plus-doc.dromara.org
 | 
					        - label: https://plus-doc.dromara.org
 | 
				
			||||||
          required: true
 | 
					          required: true
 | 
				
			||||||
@@ -35,10 +36,10 @@ body:
 | 
				
			|||||||
  - type: markdown
 | 
					  - type: markdown
 | 
				
			||||||
    attributes:
 | 
					    attributes:
 | 
				
			||||||
      label: 如何复现
 | 
					      label: 如何复现
 | 
				
			||||||
      description: 请详细告诉我们如何复现你遇到的问题
 | 
					      description: 请详细告诉我们如何复现你遇到的问题。
 | 
				
			||||||
      value: |
 | 
					      value: |
 | 
				
			||||||
        如涉及代码 可提供一个最小代码示例 并使用```附上它 或者截图均可 越详细越好<br>
 | 
					        如涉及代码,可提供一个最小代码示例,并使用```附上它,或者截图均可,越详细越好。<br>
 | 
				
			||||||
        大多数问题都是 代码编写错误问题 逻辑问题 或者用法错误等问题
 | 
					        大多数问题都是:代码编写错误问题,逻辑问题,或者用法错误等问题。
 | 
				
			||||||
    validations:
 | 
					    validations:
 | 
				
			||||||
      required: true
 | 
					      required: true
 | 
				
			||||||
  - type: textarea
 | 
					  - type: textarea
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
name: 功能建议
 | 
					name: 功能建议
 | 
				
			||||||
description: 对本项目提出一个功能建议
 | 
					description: 对本项目提出一个功能建议。
 | 
				
			||||||
title: "[功能建议]: "
 | 
					title: "[功能建议]: "
 | 
				
			||||||
labels: ["enhancement"]
 | 
					labels: ["enhancement"]
 | 
				
			||||||
body:
 | 
					body:
 | 
				
			||||||
@@ -39,5 +39,5 @@ body:
 | 
				
			|||||||
    attributes:
 | 
					    attributes:
 | 
				
			||||||
      label: 意向参与贡献
 | 
					      label: 意向参与贡献
 | 
				
			||||||
      options:
 | 
					      options:
 | 
				
			||||||
        - label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区
 | 
					        - label: 我有意向参与具体功能的开发实现并将代码贡献回到上游社区。
 | 
				
			||||||
          required: false
 | 
					          required: false
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
					  <configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
				
			||||||
    <deployment type="dockerfile">
 | 
					    <deployment type="dockerfile">
 | 
				
			||||||
      <settings>
 | 
					      <settings>
 | 
				
			||||||
        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.1.1" />
 | 
					        <option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.3.1" />
 | 
				
			||||||
        <option name="buildOnly" value="true" />
 | 
					        <option name="buildOnly" value="true" />
 | 
				
			||||||
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
 | 
					        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
 | 
				
			||||||
      </settings>
 | 
					      </settings>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
					  <configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
				
			||||||
    <deployment type="dockerfile">
 | 
					    <deployment type="dockerfile">
 | 
				
			||||||
      <settings>
 | 
					      <settings>
 | 
				
			||||||
        <option name="imageTag" value="ruoyi/ruoyi-server:5.1.1" />
 | 
					        <option name="imageTag" value="ruoyi/ruoyi-server:5.3.1" />
 | 
				
			||||||
        <option name="buildOnly" value="true" />
 | 
					        <option name="buildOnly" value="true" />
 | 
				
			||||||
        <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
 | 
					        <option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
 | 
				
			||||||
      </settings>
 | 
					      </settings>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,10 @@
 | 
				
			|||||||
<component name="ProjectRunConfigurationManager">
 | 
					<component name="ProjectRunConfigurationManager">
 | 
				
			||||||
  <configuration default="false" name="ruoyi-powerjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
					  <configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
 | 
				
			||||||
    <deployment type="dockerfile">
 | 
					    <deployment type="dockerfile">
 | 
				
			||||||
      <settings>
 | 
					      <settings>
 | 
				
			||||||
        <option name="imageTag" value="ruoyi/ruoyi-powerjob-server:5.1.1" />
 | 
					        <option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.3.1" />
 | 
				
			||||||
        <option name="buildOnly" value="true" />
 | 
					        <option name="buildOnly" value="true" />
 | 
				
			||||||
        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-powerjob-server/Dockerfile" />
 | 
					        <option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
 | 
				
			||||||
      </settings>
 | 
					      </settings>
 | 
				
			||||||
    </deployment>
 | 
					    </deployment>
 | 
				
			||||||
    <method v="2" />
 | 
					    <method v="2" />
 | 
				
			||||||
							
								
								
									
										60
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								README.md
									
									
									
									
									
								
							@@ -6,24 +6,38 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
					[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
				
			||||||
[](https://github.com/dromara/RuoYi-Vue-Plus)
 | 
					[](https://github.com/dromara/RuoYi-Vue-Plus)
 | 
				
			||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/master/LICENSE)
 | 
					[](https://gitcode.com/dromara/RuoYi-Vue-Plus)
 | 
				
			||||||
 | 
					[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
 | 
				
			||||||
[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 | 
					[](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
 | 
				
			||||||
<br>
 | 
					<br>
 | 
				
			||||||
[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
					[](https://gitee.com/dromara/RuoYi-Vue-Plus)
 | 
				
			||||||
[]()
 | 
					[]()
 | 
				
			||||||
[]()
 | 
					[]()
 | 
				
			||||||
[]()
 | 
					[]()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
 | 
					> Dromara RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 `分布式集群与多租户` 场景全方位升级(不兼容原框架)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
 | 
					> 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可<br>
 | 
				
			||||||
活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
 | 
					活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
 | 
					> 系统演示: [传送门](https://plus-doc.dromara.org/#/common/demo_system)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 前端项目地址: [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
 | 
					> 官方前端项目地址: [gitee](https://gitee.com/JavaLionLi/plus-ui) - [github](https://github.com/JavaLionLi/plus-ui) - [gitcode](https://gitcode.com/dromara/plus-ui)<br>
 | 
				
			||||||
 | 
					> 成员前端项目地址: 基于vben5 [ruoyi-plus-vben5](https://gitee.com/dapppp/ruoyi-plus-vben5)<br>
 | 
				
			||||||
 | 
					> 成员前端项目地址: 基于soybean [ruoyi-plus-soybean](https://gitee.com/xlsea/ruoyi-plus-soybean)<br>
 | 
				
			||||||
 | 
					> 成员项目地址: 删除多租户与工作流 [RuoYi-Vue-Plus-Single](https://gitee.com/ColorDreams/RuoYi-Vue-Plus-Single)<br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
> 文档地址: [plus-doc](https://plus-doc.dromara.org) - [plus-doc(国内备用)](https://dromara.gitee.io/plus-doc)
 | 
					> 文档地址: [plus-doc](https://plus-doc.dromara.org) 文档在华为云上如果打不开大概率是DNS问题 可以尝试切换网络等方式(或者科学上网)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 赞助商
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
 | 
				
			||||||
 | 
					CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
 | 
				
			||||||
 | 
					数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
 | 
				
			||||||
 | 
					引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
 | 
				
			||||||
 | 
					<font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br>
 | 
				
			||||||
 | 
					Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
 | 
				
			||||||
 | 
					[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 本框架与RuoYi的功能差异
 | 
					# 本框架与RuoYi的功能差异
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,7 +50,7 @@
 | 
				
			|||||||
| 权限认证        | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展                                                                                  | Spring Security 配置繁琐扩展性极差                                                          |
 | 
					| 权限认证        | 采用 Sa-Token、Jwt 静态使用功能齐全 低耦合 高扩展                                                                                  | Spring Security 配置繁琐扩展性极差                                                          |
 | 
				
			||||||
| 权限注解        | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式        | 只支持是否存在匹配                                                                          |
 | 
					| 权限注解        | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式        | 只支持是否存在匹配                                                                          |
 | 
				
			||||||
| 三方鉴权        | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证                                                                               | 无                                                                                  |
 | 
					| 三方鉴权        | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证                                                                               | 无                                                                                  |
 | 
				
			||||||
| 关系数据库支持     | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换                                                              | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换                                                    |
 | 
					| 关系数据库支持     | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例)      | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换                                                    |
 | 
				
			||||||
| 缓存数据库       | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列                                                                             | Redis 简单 get set 支持                                                                |
 | 
					| 缓存数据库       | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列                                                                             | Redis 简单 get set 支持                                                                |
 | 
				
			||||||
| Redis客户端    | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题              |
 | 
					| Redis客户端    | 采用 Redisson Redis官方推荐 基于Netty的客户端工具<br/>支持Redis 90%以上的命令 底层优化规避很多不正确的用法 例如: keys被转换为scan<br/>支持单机、哨兵、单主集群、多主集群等模式 | Lettuce + RedisTemplate 支持模式少 工具使用繁琐<br/>连接池采用 common-pool Bug多经常性出问题              |
 | 
				
			||||||
| 缓存注解        | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存                                      | 需手动编写Redis代码逻辑                                                                     |
 | 
					| 缓存注解        | 采用 Spring-Cache 注解 对其扩展了实现支持了更多功能<br/>例如 过期时间 最大空闲时间 组最大长度等 只需一个注解即可完成数据自动缓存                                      | 需手动编写Redis代码逻辑                                                                     |
 | 
				
			||||||
@@ -48,22 +62,24 @@
 | 
				
			|||||||
| 数据加解密       | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等                                              | 无                                                                                  |
 | 
					| 数据加解密       | 采用 注解 + mybatis 拦截器 对存取数据期间自动加解密<br/>支持多种策略 如BASE64、AES、RSA、SM2、SM4等                                              | 无                                                                                  |
 | 
				
			||||||
| 接口传输加密      | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性                                                                     | 无                                                                                  |
 | 
					| 接口传输加密      | 采用 动态 AES + RSA 加密请求 body 每一次请求秘钥都不同大幅度降低可破解性                                                                     | 无                                                                                  |
 | 
				
			||||||
| 数据翻译        | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现                   | 无                                                                                  |
 | 
					| 数据翻译        | 采用 注解 + jackson 序列化期间动态修改数据 数据进行翻译<br/>支持多种模式: `映射翻译` `直接翻译` `其他扩展条件翻译` 接口化两步即可完成自定义扩展 内置多种翻译实现                   | 无                                                                                  |
 | 
				
			||||||
| 多数据源框架      | 采用 dynamic-datasource 支持世面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源            | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差                                                     |
 | 
					| 多数据源框架      | 采用 dynamic-datasource 支持市面大部分数据库<br/>通过yml配置即可动态管理异构不同种类的数据库 也可通过前端页面添加数据源<br/>支持spel表达式从请求头参数等条件切换数据源            | 基于 druid 手动编写代码配置数据源 配置繁琐 支持性差                                                     |
 | 
				
			||||||
| 多数据源事务      | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚                                                                          | 不支持                                                                                |
 | 
					| 多数据源事务      | 采用 dynamic-datasource 支持多数据源不同种类的数据库事务回滚                                                                          | 不支持                                                                                |
 | 
				
			||||||
| 数据库连接池      | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下                                                                        | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般                                               |
 | 
					| 数据库连接池      | 采用 HikariCP Spring官方内置连接池 配置简单 以性能与稳定性闻名天下                                                                        | 采用 druid bug众多 社区维护差 活跃度低 配置众多繁琐性能一般                                               |
 | 
				
			||||||
| 数据库主键       | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁                                                                  | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一                                                     |
 | 
					| 数据库主键       | 采用 雪花ID 基于时间戳的 有序增长 唯一ID 再也不用为分库分表 数据合并主键冲突重复而发愁                                                                  | 采用 数据库自增ID 支持数据量有限 不支持多数据源主键唯一                                                     |
 | 
				
			||||||
| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物                                                         | 无                                                                                  |
 | 
					| WebSocket协议 | 基于 Spring 封装的 WebSocket 协议 扩展了Token鉴权与分布式会话同步 不再只是基于单机的废物                                                         | 无                                                                                  |
 | 
				
			||||||
 | 
					| SSE推送       | 采用 Spring SSE 实现 扩展了Token鉴权与分布式会话同步                                                                               | 无                                                                                  |
 | 
				
			||||||
| 序列化         | 采用 Jackson Spring官方内置序列化 靠谱!!!                                                                                    | 采用 fastjson bugjson 远近闻名                                                           | 
 | 
					| 序列化         | 采用 Jackson Spring官方内置序列化 靠谱!!!                                                                                    | 采用 fastjson bugjson 远近闻名                                                           | 
 | 
				
			||||||
| 分布式幂等       | 参考美团GTIS防重系统简化实现(细节可看文档)                                                                                          | 手动编写注解基于aop实现                                                                      |
 | 
					| 分布式幂等       | 参考美团GTIS防重系统简化实现(细节可看文档)                                                                                          | 手动编写注解基于aop实现                                                                      |
 | 
				
			||||||
| 分布式锁        | 采用 Lock4j 底层基于 Redisson                                                                                           | 无                                                                                  |
 | 
					| 分布式锁        | 采用 Lock4j 底层基于 Redisson                                                                                           | 无                                                                                  |
 | 
				
			||||||
| 分布式任务调度     | 采用 PowerJob 天生支持分布式 统一的管理中心                                                                                       | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造                                                   | 
 | 
					| 分布式任务调度     | 采用 SnailJob 天生支持分布式 统一的管理中心 支持多种数据库 支持分片重试DAG任务流等                                                                 | 采用 Quartz 基于数据库锁性能差 集群需要做很多配置与改造                                                   | 
 | 
				
			||||||
| 文件存储        | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储                                                     | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应                                                    |
 | 
					| 文件存储        | 采用 Minio 分布式文件存储 天生支持多机、多硬盘、多分片、多副本存储<br/>支持权限管理 安全可靠 文件可加密存储                                                     | 采用 本机文件存储 文件裸漏 易丢失泄漏 不支持集群有单点效应                                                    |
 | 
				
			||||||
| 云存储         | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家                                                                          | 不支持                                                                                |
 | 
					| 云存储         | 采用 AWS S3 协议客户端 支持 七牛、阿里、腾讯 等一切支持S3协议的厂家                                                                          | 不支持                                                                                |
 | 
				
			||||||
| 短信          | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用                                                                 | 不支持                                                                                |
 | 
					| 短信          | 采用 sms4j 短信融合包 支持数十种短信厂家 只需在yml配置好厂家密钥即可使用 可多厂家共用                                                                 | 不支持                                                                                |
 | 
				
			||||||
| 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                |
 | 
					| 邮件          | 采用 mail-api 通用协议支持大部分邮件厂商                                                                                         | 不支持                                                                                |
 | 
				
			||||||
| 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                | 
 | 
					| 接口文档        | 采用 SpringDoc、javadoc 无注解零入侵基于java注释<br/>只需把注释写好 无需再写一大堆的文档注解了                                                     | 采用 Springfox 已停止维护 需要编写大量的注解来支持文档生成                                                | 
 | 
				
			||||||
| 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    |
 | 
					| 校验框架        | 采用 Validation 支持注解与工具类校验 注解支持国际化                                                                                  | 仅支持注解 且注解不支持国际化                                                                    |
 | 
				
			||||||
| Excel框架     | 采用 Alibaba EasyExcel 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                               | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           |
 | 
					| Excel框架     | 采用 FastExcel(原Alibaba EasyExcel) 基于插件化<br/>框架对其增加了很多功能 例如 自动合并相同内容 自动排列布局 字典翻译等                                   | 基于 POI 手写实现 功能有限 复杂 扩展性差                                                           |
 | 
				
			||||||
 | 
					| 工作流支持       | 支持各种复杂审批 转办 委派 加减签 会签 或签 票签 等功能                                                                                   | 无                                                                                  |
 | 
				
			||||||
| 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            | 
 | 
					| 工具类框架       | 采用 Hutool、Lombok 上百种工具覆盖90%的使用需求 基于注解自动生成 get set 等简化框架大量代码                                                       | 手写工具稳定性差易出问题 工具数量有限 代码臃肿需自己手写 get set 等                                            | 
 | 
				
			||||||
| 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  | 
 | 
					| 监控框架        | 采用 SpringBoot-Admin 基于SpringBoot官方 actuator 探针机制<br/>实时监控服务状态 框架还为其扩展了在线日志查看监控                                    | 无                                                                                  | 
 | 
				
			||||||
| 链路追踪        | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点                                            | 无                                                                                  |
 | 
					| 链路追踪        | 采用 Apache SkyWalking 还在为请求不知道去哪了 到哪出了问题而烦恼吗<br/>用了它即可实时查看请求经过的每一处每一个节点                                            | 无                                                                                  |
 | 
				
			||||||
@@ -100,7 +116,6 @@
 | 
				
			|||||||
| 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               |
 | 
					| 系统接口   | 根据业务代码自动生成相关的api接口文档                                                 | 支持  | 支持               |
 | 
				
			||||||
| 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 |
 | 
					| 服务监控   | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等                                  | 支持  | 仅支持单机CPU、内存、磁盘监控 |
 | 
				
			||||||
| 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               |
 | 
					| 缓存监控   | 对系统的缓存信息查询,命令统计等。                                                    | 支持  | 支持               |
 | 
				
			||||||
| 在线构建器  | 拖动表单元素生成相应的HTML代码。                                                   | 支持  | 支持               |
 | 
					 | 
				
			||||||
| 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              |
 | 
					| 使用案例   | 系统的一些功能案例                                                            | 支持  | 不支持              |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 参考文档
 | 
					## 参考文档
 | 
				
			||||||
@@ -116,12 +131,11 @@
 | 
				
			|||||||
>[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
 | 
					>[部署项目 必看](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
 | 
				
			||||||
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
 | 
					>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy](https://plus-doc.dromara.org/#/ruoyi-vue-plus/quickstart/deploy)
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
 | 
					>[如何加群](https://plus-doc.dromara.org/#/common/add_group)
 | 
				
			||||||
 | 
					>>[https://plus-doc.dromara.org/#/common/add_group](https://plus-doc.dromara.org/#/common/add_group)
 | 
				
			||||||
 | 
					>
 | 
				
			||||||
>[参考文档 Wiki](https://plus-doc.dromara.org)
 | 
					>[参考文档 Wiki](https://plus-doc.dromara.org)
 | 
				
			||||||
>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
 | 
					>>[https://plus-doc.dromara.org](https://plus-doc.dromara.org)
 | 
				
			||||||
> 
 | 
					 | 
				
			||||||
>[参考文档(国内备份)](https://dromara.gitee.io/plus-doc)
 | 
					 | 
				
			||||||
>>[https://dromara.gitee.io/plus-doc](https://dromara.gitee.io/plus-doc)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 软件架构图
 | 
					## 软件架构图
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -131,18 +145,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
 | 
					[参与贡献的方式 https://plus-doc.dromara.org/#/common/contribution](https://plus-doc.dromara.org/#/common/contribution)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### 其他
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
* 定期同步升级 RuoYi-Vue 有用的更新
 | 
					 | 
				
			||||||
* GitHub 地址 [RuoYi-Vue-Plus](https://github.com/dromara/RuoYi-Vue-Plus)
 | 
					 | 
				
			||||||
* 微服务 分支 [RuoYi-Cloud-Plus](https://gitee.com/JavaLionLi/RuoYi-Cloud-Plus)
 | 
					 | 
				
			||||||
* 前端项目 地址 [plus-ui](https://gitee.com/JavaLionLi/plus-ui)
 | 
					 | 
				
			||||||
* 用户扩展项目 [扩展项目列表](https://plus-doc.dromara.org/#/ruoyi-vue-plus/extend-project/list)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## 加群与捐献
 | 
					 | 
				
			||||||
>[加群与捐献](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
 | 
					 | 
				
			||||||
>>[https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat](https://plus-doc.dromara.org/#/ruoyi-vue-plus/other/group_chat)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
## 捐献作者
 | 
					## 捐献作者
 | 
				
			||||||
作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  
 | 
					作者为兼职做开源,平时还需要工作,如果帮到了您可以请作者吃个盒饭  
 | 
				
			||||||
<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
 | 
					<img src="https://foruda.gitee.com/images/1678975784848381069/d8661ed9_1766278.png" width="300px" height="450px" />
 | 
				
			||||||
@@ -168,8 +170,8 @@
 | 
				
			|||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
|  |  |
 | 
					|  |  |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										156
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										156
									
								
								pom.xml
									
									
									
									
									
								
							@@ -10,52 +10,55 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <name>RuoYi-Vue-Plus</name>
 | 
					    <name>RuoYi-Vue-Plus</name>
 | 
				
			||||||
    <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
 | 
					    <url>https://gitee.com/dromara/RuoYi-Vue-Plus</url>
 | 
				
			||||||
    <description>RuoYi-Vue-Plus多租户管理系统</description>
 | 
					    <description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <properties>
 | 
					    <properties>
 | 
				
			||||||
        <revision>5.1.1</revision>
 | 
					        <revision>5.3.1</revision>
 | 
				
			||||||
        <spring-boot.version>3.1.5</spring-boot.version>
 | 
					        <spring-boot.version>3.4.5</spring-boot.version>
 | 
				
			||||||
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
					        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 | 
				
			||||||
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
					        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 | 
				
			||||||
        <java.version>17</java.version>
 | 
					        <java.version>17</java.version>
 | 
				
			||||||
        <spring-boot.mybatis>3.0.2</spring-boot.mybatis>
 | 
					        <mybatis.version>3.5.16</mybatis.version>
 | 
				
			||||||
        <springdoc.version>2.2.0</springdoc.version>
 | 
					        <springdoc.version>2.8.5</springdoc.version>
 | 
				
			||||||
        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
 | 
					        <therapi-javadoc.version>0.15.0</therapi-javadoc.version>
 | 
				
			||||||
        <poi.version>5.2.3</poi.version>
 | 
					        <fastexcel.version>1.2.0</fastexcel.version>
 | 
				
			||||||
        <easyexcel.version>3.3.2</easyexcel.version>
 | 
					 | 
				
			||||||
        <velocity.version>2.3</velocity.version>
 | 
					        <velocity.version>2.3</velocity.version>
 | 
				
			||||||
        <satoken.version>1.37.0</satoken.version>
 | 
					        <satoken.version>1.42.0</satoken.version>
 | 
				
			||||||
        <mybatis-plus.version>3.5.4</mybatis-plus.version>
 | 
					        <mybatis-plus.version>3.5.11</mybatis-plus.version>
 | 
				
			||||||
        <p6spy.version>3.9.1</p6spy.version>
 | 
					        <p6spy.version>3.9.1</p6spy.version>
 | 
				
			||||||
        <hutool.version>5.8.22</hutool.version>
 | 
					        <hutool.version>5.8.35</hutool.version>
 | 
				
			||||||
        <okhttp.version>4.10.0</okhttp.version>
 | 
					        <spring-boot-admin.version>3.4.5</spring-boot-admin.version>
 | 
				
			||||||
        <spring-boot-admin.version>3.1.7</spring-boot-admin.version>
 | 
					        <redisson.version>3.45.1</redisson.version>
 | 
				
			||||||
        <redisson.version>3.24.1</redisson.version>
 | 
					        <lock4j.version>2.2.7</lock4j.version>
 | 
				
			||||||
        <lock4j.version>2.2.5</lock4j.version>
 | 
					        <dynamic-ds.version>4.3.1</dynamic-ds.version>
 | 
				
			||||||
        <dynamic-ds.version>4.2.0</dynamic-ds.version>
 | 
					        <snailjob.version>1.5.0</snailjob.version>
 | 
				
			||||||
        <alibaba-ttl.version>2.14.2</alibaba-ttl.version>
 | 
					        <mapstruct-plus.version>1.4.6</mapstruct-plus.version>
 | 
				
			||||||
        <powerjob.version>4.3.3</powerjob.version>
 | 
					 | 
				
			||||||
        <mapstruct-plus.version>1.3.5</mapstruct-plus.version>
 | 
					 | 
				
			||||||
        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
 | 
					        <mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
 | 
				
			||||||
        <lombok.version>1.18.30</lombok.version>
 | 
					        <lombok.version>1.18.36</lombok.version>
 | 
				
			||||||
        <bouncycastle.version>1.76</bouncycastle.version>
 | 
					        <bouncycastle.version>1.80</bouncycastle.version>
 | 
				
			||||||
        <justauth.version>1.16.5</justauth.version>
 | 
					        <justauth.version>1.16.7</justauth.version>
 | 
				
			||||||
        <!-- 离线IP地址定位库 -->
 | 
					        <!-- 离线IP地址定位库 -->
 | 
				
			||||||
        <ip2region.version>2.7.0</ip2region.version>
 | 
					        <ip2region.version>2.7.0</ip2region.version>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- OSS 配置 -->
 | 
					        <!-- OSS 配置 -->
 | 
				
			||||||
        <aws-java-sdk-s3.version>1.12.540</aws-java-sdk-s3.version>
 | 
					        <aws.sdk.version>2.28.22</aws.sdk.version>
 | 
				
			||||||
        <!-- SMS 配置 -->
 | 
					        <!-- SMS 配置 -->
 | 
				
			||||||
        <sms4j.version>2.2.0</sms4j.version>
 | 
					        <sms4j.version>3.3.4</sms4j.version>
 | 
				
			||||||
        <!-- 限制框架中的fastjson版本 -->
 | 
					        <!-- 限制框架中的fastjson版本 -->
 | 
				
			||||||
        <fastjson.version>1.2.83</fastjson.version>
 | 
					        <fastjson.version>1.2.83</fastjson.version>
 | 
				
			||||||
 | 
					        <!-- 面向运行时的D-ORM依赖 -->
 | 
				
			||||||
 | 
					        <anyline.version>8.7.2-20250101</anyline.version>
 | 
				
			||||||
 | 
					        <!-- 工作流配置 -->
 | 
				
			||||||
 | 
					        <warm-flow.version>1.7.2</warm-flow.version>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- 插件版本 -->
 | 
					        <!-- 插件版本 -->
 | 
				
			||||||
        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
 | 
					        <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
 | 
				
			||||||
        <maven-war-plugin.version>3.2.2</maven-war-plugin.version>
 | 
					        <maven-war-plugin.version>3.2.2</maven-war-plugin.version>
 | 
				
			||||||
        <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
 | 
					        <maven-compiler-plugin.version>3.11.0</maven-compiler-plugin.version>
 | 
				
			||||||
        <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
 | 
					        <maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
 | 
				
			||||||
        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
 | 
					        <flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
 | 
				
			||||||
 | 
					        <!-- 打包默认跳过测试 -->
 | 
				
			||||||
 | 
					        <skipTests>true</skipTests>
 | 
				
			||||||
    </properties>
 | 
					    </properties>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <profiles>
 | 
					    <profiles>
 | 
				
			||||||
@@ -65,6 +68,8 @@
 | 
				
			|||||||
                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
					                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
				
			||||||
                <profiles.active>local</profiles.active>
 | 
					                <profiles.active>local</profiles.active>
 | 
				
			||||||
                <logging.level>info</logging.level>
 | 
					                <logging.level>info</logging.level>
 | 
				
			||||||
 | 
					                <monitor.username>ruoyi</monitor.username>
 | 
				
			||||||
 | 
					                <monitor.password>123456</monitor.password>
 | 
				
			||||||
            </properties>
 | 
					            </properties>
 | 
				
			||||||
        </profile>
 | 
					        </profile>
 | 
				
			||||||
        <profile>
 | 
					        <profile>
 | 
				
			||||||
@@ -73,6 +78,8 @@
 | 
				
			|||||||
                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
					                <!-- 环境标识,需要与配置文件的名称相对应 -->
 | 
				
			||||||
                <profiles.active>dev</profiles.active>
 | 
					                <profiles.active>dev</profiles.active>
 | 
				
			||||||
                <logging.level>info</logging.level>
 | 
					                <logging.level>info</logging.level>
 | 
				
			||||||
 | 
					                <monitor.username>ruoyi</monitor.username>
 | 
				
			||||||
 | 
					                <monitor.password>123456</monitor.password>
 | 
				
			||||||
            </properties>
 | 
					            </properties>
 | 
				
			||||||
            <activation>
 | 
					            <activation>
 | 
				
			||||||
                <!-- 默认环境 -->
 | 
					                <!-- 默认环境 -->
 | 
				
			||||||
@@ -84,6 +91,8 @@
 | 
				
			|||||||
            <properties>
 | 
					            <properties>
 | 
				
			||||||
                <profiles.active>prod</profiles.active>
 | 
					                <profiles.active>prod</profiles.active>
 | 
				
			||||||
                <logging.level>warn</logging.level>
 | 
					                <logging.level>warn</logging.level>
 | 
				
			||||||
 | 
					                <monitor.username>ruoyi</monitor.username>
 | 
				
			||||||
 | 
					                <monitor.password>123456</monitor.password>
 | 
				
			||||||
            </properties>
 | 
					            </properties>
 | 
				
			||||||
        </profile>
 | 
					        </profile>
 | 
				
			||||||
    </profiles>
 | 
					    </profiles>
 | 
				
			||||||
@@ -110,6 +119,18 @@
 | 
				
			|||||||
                <scope>import</scope>
 | 
					                <scope>import</scope>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Warm-Flow国产工作流引擎, 在线文档:http://warm-flow.cn/ -->
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>org.dromara.warm</groupId>
 | 
				
			||||||
 | 
					                <artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
 | 
				
			||||||
 | 
					                <version>${warm-flow.version}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>org.dromara.warm</groupId>
 | 
				
			||||||
 | 
					                <artifactId>warm-flow-plugin-ui-sb-web</artifactId>
 | 
				
			||||||
 | 
					                <version>${warm-flow.version}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- JustAuth 的依赖配置-->
 | 
					            <!-- JustAuth 的依赖配置-->
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>me.zhyd.oauth</groupId>
 | 
					                <groupId>me.zhyd.oauth</groupId>
 | 
				
			||||||
@@ -145,25 +166,9 @@
 | 
				
			|||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>org.apache.poi</groupId>
 | 
					                <groupId>cn.idev.excel</groupId>
 | 
				
			||||||
                <artifactId>poi</artifactId>
 | 
					                <artifactId>fastexcel</artifactId>
 | 
				
			||||||
                <version>${poi.version}</version>
 | 
					                <version>${fastexcel.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					 | 
				
			||||||
            <dependency>
 | 
					 | 
				
			||||||
                <groupId>org.apache.poi</groupId>
 | 
					 | 
				
			||||||
                <artifactId>poi-ooxml</artifactId>
 | 
					 | 
				
			||||||
                <version>${poi.version}</version>
 | 
					 | 
				
			||||||
            </dependency>
 | 
					 | 
				
			||||||
            <dependency>
 | 
					 | 
				
			||||||
                <groupId>com.alibaba</groupId>
 | 
					 | 
				
			||||||
                <artifactId>easyexcel</artifactId>
 | 
					 | 
				
			||||||
                <version>${easyexcel.version}</version>
 | 
					 | 
				
			||||||
                <exclusions>
 | 
					 | 
				
			||||||
                    <exclusion>
 | 
					 | 
				
			||||||
                        <groupId>org.apache.poi</groupId>
 | 
					 | 
				
			||||||
                        <artifactId>poi-ooxml-schemas</artifactId>
 | 
					 | 
				
			||||||
                    </exclusion>
 | 
					 | 
				
			||||||
                </exclusions>
 | 
					 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- velocity代码生成使用模板 -->
 | 
					            <!-- velocity代码生成使用模板 -->
 | 
				
			||||||
@@ -205,14 +210,20 @@
 | 
				
			|||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>org.mybatis.spring.boot</groupId>
 | 
					                <groupId>org.mybatis</groupId>
 | 
				
			||||||
                <artifactId>mybatis-spring-boot-starter</artifactId>
 | 
					                <artifactId>mybatis</artifactId>
 | 
				
			||||||
                <version>${spring-boot.mybatis}</version>
 | 
					                <version>${mybatis.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>com.baomidou</groupId>
 | 
					                <groupId>com.baomidou</groupId>
 | 
				
			||||||
                <artifactId>mybatis-plus-boot-starter</artifactId>
 | 
					                <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
 | 
				
			||||||
 | 
					                <version>${mybatis-plus.version}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>com.baomidou</groupId>
 | 
				
			||||||
 | 
					                <artifactId>mybatis-plus-jsqlparser</artifactId>
 | 
				
			||||||
                <version>${mybatis-plus.version}</version>
 | 
					                <version>${mybatis-plus.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -229,16 +240,23 @@
 | 
				
			|||||||
                <version>${p6spy.version}</version>
 | 
					                <version>${p6spy.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!--  AWS SDK for Java 2.x  -->
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>com.squareup.okhttp3</groupId>
 | 
					                <groupId>software.amazon.awssdk</groupId>
 | 
				
			||||||
                <artifactId>okhttp</artifactId>
 | 
					                <artifactId>s3</artifactId>
 | 
				
			||||||
                <version>${okhttp.version}</version>
 | 
					                <version>${aws.sdk.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					            <!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>com.amazonaws</groupId>
 | 
					                <groupId>software.amazon.awssdk</groupId>
 | 
				
			||||||
                <artifactId>aws-java-sdk-s3</artifactId>
 | 
					                <artifactId>s3-transfer-manager</artifactId>
 | 
				
			||||||
                <version>${aws-java-sdk-s3.version}</version>
 | 
					                <version>${aws.sdk.version}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					            <!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 -->
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>software.amazon.awssdk</groupId>
 | 
				
			||||||
 | 
					                <artifactId>netty-nio-client</artifactId>
 | 
				
			||||||
 | 
					                <version>${aws.sdk.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
            <!--短信sms4j-->
 | 
					            <!--短信sms4j-->
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
@@ -271,22 +289,16 @@
 | 
				
			|||||||
                <version>${lock4j.version}</version>
 | 
					                <version>${lock4j.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- PowerJob -->
 | 
					            <!-- SnailJob Client -->
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>tech.powerjob</groupId>
 | 
					                <groupId>com.aizuda</groupId>
 | 
				
			||||||
                <artifactId>powerjob-worker-spring-boot-starter</artifactId>
 | 
					                <artifactId>snail-job-client-starter</artifactId>
 | 
				
			||||||
                <version>${powerjob.version}</version>
 | 
					                <version>${snailjob.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>tech.powerjob</groupId>
 | 
					                <groupId>com.aizuda</groupId>
 | 
				
			||||||
                <artifactId>powerjob-official-processors</artifactId>
 | 
					                <artifactId>snail-job-client-job-core</artifactId>
 | 
				
			||||||
                <version>${powerjob.version}</version>
 | 
					                <version>${snailjob.version}</version>
 | 
				
			||||||
            </dependency>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <dependency>
 | 
					 | 
				
			||||||
                <groupId>com.alibaba</groupId>
 | 
					 | 
				
			||||||
                <artifactId>transmittable-thread-local</artifactId>
 | 
					 | 
				
			||||||
                <version>${alibaba-ttl.version}</version>
 | 
					 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- 加密包引入 -->
 | 
					            <!-- 加密包引入 -->
 | 
				
			||||||
@@ -339,6 +351,13 @@
 | 
				
			|||||||
                <version>${revision}</version>
 | 
					                <version>${revision}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!--  工作流模块  -->
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>org.dromara</groupId>
 | 
				
			||||||
 | 
					                <artifactId>ruoyi-workflow</artifactId>
 | 
				
			||||||
 | 
					                <version>${revision}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </dependencies>
 | 
					        </dependencies>
 | 
				
			||||||
    </dependencyManagement>
 | 
					    </dependencyManagement>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -355,7 +374,7 @@
 | 
				
			|||||||
            <plugin>
 | 
					            <plugin>
 | 
				
			||||||
                <groupId>org.apache.maven.plugins</groupId>
 | 
					                <groupId>org.apache.maven.plugins</groupId>
 | 
				
			||||||
                <artifactId>maven-compiler-plugin</artifactId>
 | 
					                <artifactId>maven-compiler-plugin</artifactId>
 | 
				
			||||||
                <version>${maven-compiler-plugin.verison}</version>
 | 
					                <version>${maven-compiler-plugin.version}</version>
 | 
				
			||||||
                <configuration>
 | 
					                <configuration>
 | 
				
			||||||
                    <source>${java.version}</source>
 | 
					                    <source>${java.version}</source>
 | 
				
			||||||
                    <target>${java.version}</target>
 | 
					                    <target>${java.version}</target>
 | 
				
			||||||
@@ -398,6 +417,7 @@
 | 
				
			|||||||
                <artifactId>maven-surefire-plugin</artifactId>
 | 
					                <artifactId>maven-surefire-plugin</artifactId>
 | 
				
			||||||
                <version>${maven-surefire-plugin.version}</version>
 | 
					                <version>${maven-surefire-plugin.version}</version>
 | 
				
			||||||
                <configuration>
 | 
					                <configuration>
 | 
				
			||||||
 | 
					                    <argLine>-Dfile.encoding=UTF-8</argLine>
 | 
				
			||||||
                    <!-- 根据打包环境执行对应的@Tag测试方法 -->
 | 
					                    <!-- 根据打包环境执行对应的@Tag测试方法 -->
 | 
				
			||||||
                    <groups>${profiles.active}</groups>
 | 
					                    <groups>${profiles.active}</groups>
 | 
				
			||||||
                    <!-- 排除标签 -->
 | 
					                    <!-- 排除标签 -->
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
 | 
					# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
 | 
				
			||||||
 | 
					FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
 | 
				
			||||||
 | 
					#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds
 | 
				
			||||||
#FROM findepi/graalvm:java17-native
 | 
					#FROM findepi/graalvm:java17-native
 | 
				
			||||||
FROM openjdk:17.0.2-oraclelinux8
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
MAINTAINER Lion Li
 | 
					LABEL maintainer="Lion Li"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RUN mkdir -p /ruoyi/server/logs \
 | 
					RUN mkdir -p /ruoyi/server/logs \
 | 
				
			||||||
    /ruoyi/server/temp \
 | 
					    /ruoyi/server/temp \
 | 
				
			||||||
@@ -9,16 +11,23 @@ RUN mkdir -p /ruoyi/server/logs \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
WORKDIR /ruoyi/server
 | 
					WORKDIR /ruoyi/server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8
 | 
					ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPOSE ${SERVER_PORT}
 | 
					EXPOSE ${SERVER_PORT}
 | 
				
			||||||
 | 
					# 暴露 snail job 客户端端口 用于定时任务调度中心通信
 | 
				
			||||||
 | 
					EXPOSE ${SNAIL_PORT}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ADD ./target/ruoyi-admin.jar ./app.jar
 | 
					ADD ./target/ruoyi-admin.jar ./app.jar
 | 
				
			||||||
 | 
					# 工作流字体文件
 | 
				
			||||||
 | 
					ADD ./zhFonts/ /usr/share/fonts/zhFonts/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENTRYPOINT ["java", \
 | 
					SHELL ["/bin/bash", "-c"]
 | 
				
			||||||
            "-Djava.security.egd=file:/dev/./urandom", \
 | 
					
 | 
				
			||||||
            "-Dserver.port=${SERVER_PORT}", \
 | 
					ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
 | 
				
			||||||
 | 
					           -Dsnail-job.port=${SNAIL_PORT} \
 | 
				
			||||||
           # 应用名称 如果想区分集群节点监控 改成不同的名称即可
 | 
					           # 应用名称 如果想区分集群节点监控 改成不同的名称即可
 | 
				
			||||||
#            "-Dskywalking.agent.service_name=ruoyi-server", \
 | 
					           #-Dskywalking.agent.service_name=ruoyi-server \
 | 
				
			||||||
#            "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
 | 
					           #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
 | 
				
			||||||
            "-jar", "app.jar"]
 | 
					           -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
 | 
				
			||||||
 | 
					           -jar app.jar
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,21 +22,28 @@
 | 
				
			|||||||
            <groupId>com.mysql</groupId>
 | 
					            <groupId>com.mysql</groupId>
 | 
				
			||||||
            <artifactId>mysql-connector-j</artifactId>
 | 
					            <artifactId>mysql-connector-j</artifactId>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
        <!-- Oracle -->
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					<!--        <!– mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 –>-->
 | 
				
			||||||
            <groupId>com.oracle.database.jdbc</groupId>
 | 
					<!--        <!– Oracle –>-->
 | 
				
			||||||
            <artifactId>ojdbc8</artifactId>
 | 
					<!--        <dependency>-->
 | 
				
			||||||
        </dependency>
 | 
					<!--            <groupId>com.oracle.database.jdbc</groupId>-->
 | 
				
			||||||
        <!-- PostgreSql -->
 | 
					<!--            <artifactId>ojdbc8</artifactId>-->
 | 
				
			||||||
        <dependency>
 | 
					<!--        </dependency>-->
 | 
				
			||||||
            <groupId>org.postgresql</groupId>
 | 
					<!--        <!– 兼容oracle低版本 –>-->
 | 
				
			||||||
            <artifactId>postgresql</artifactId>
 | 
					<!--        <dependency>-->
 | 
				
			||||||
        </dependency>
 | 
					<!--            <groupId>com.oracle.database.nls</groupId>-->
 | 
				
			||||||
        <!-- SqlServer -->
 | 
					<!--            <artifactId>orai18n</artifactId>-->
 | 
				
			||||||
        <dependency>
 | 
					<!--        </dependency>-->
 | 
				
			||||||
            <groupId>com.microsoft.sqlserver</groupId>
 | 
					<!--        <!– PostgreSql –>-->
 | 
				
			||||||
            <artifactId>mssql-jdbc</artifactId>
 | 
					<!--        <dependency>-->
 | 
				
			||||||
        </dependency>
 | 
					<!--            <groupId>org.postgresql</groupId>-->
 | 
				
			||||||
 | 
					<!--            <artifactId>postgresql</artifactId>-->
 | 
				
			||||||
 | 
					<!--        </dependency>-->
 | 
				
			||||||
 | 
					<!--        <!– SqlServer –>-->
 | 
				
			||||||
 | 
					<!--        <dependency>-->
 | 
				
			||||||
 | 
					<!--            <groupId>com.microsoft.sqlserver</groupId>-->
 | 
				
			||||||
 | 
					<!--            <artifactId>mssql-jdbc</artifactId>-->
 | 
				
			||||||
 | 
					<!--        </dependency>-->
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>org.dromara</groupId>
 | 
					            <groupId>org.dromara</groupId>
 | 
				
			||||||
@@ -48,6 +55,15 @@
 | 
				
			|||||||
            <artifactId>ruoyi-common-social</artifactId>
 | 
					            <artifactId>ruoyi-common-social</artifactId>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.dromara</groupId>
 | 
				
			||||||
 | 
					            <artifactId>ruoyi-common-ratelimiter</artifactId>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.dromara</groupId>
 | 
				
			||||||
 | 
					            <artifactId>ruoyi-common-mail</artifactId>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>org.dromara</groupId>
 | 
					            <groupId>org.dromara</groupId>
 | 
				
			||||||
@@ -71,6 +87,12 @@
 | 
				
			|||||||
            <artifactId>ruoyi-demo</artifactId>
 | 
					            <artifactId>ruoyi-demo</artifactId>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!--  工作流模块  -->
 | 
				
			||||||
 | 
					        <dependency>
 | 
				
			||||||
 | 
					            <groupId>org.dromara</groupId>
 | 
				
			||||||
 | 
					            <artifactId>ruoyi-workflow</artifactId>
 | 
				
			||||||
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>de.codecentric</groupId>
 | 
					            <groupId>de.codecentric</groupId>
 | 
				
			||||||
            <artifactId>spring-boot-admin-starter-client</artifactId>
 | 
					            <artifactId>spring-boot-admin-starter-client</artifactId>
 | 
				
			||||||
@@ -82,11 +104,6 @@
 | 
				
			|||||||
            <scope>test</scope>
 | 
					            <scope>test</scope>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					 | 
				
			||||||
            <groupId>me.zhyd.oauth</groupId>
 | 
					 | 
				
			||||||
            <artifactId>JustAuth</artifactId>
 | 
					 | 
				
			||||||
        </dependency>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- skywalking 整合 logback -->
 | 
					        <!-- skywalking 整合 logback -->
 | 
				
			||||||
<!--        <dependency>-->
 | 
					<!--        <dependency>-->
 | 
				
			||||||
<!--            <groupId>org.apache.skywalking</groupId>-->
 | 
					<!--            <groupId>org.apache.skywalking</groupId>-->
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,9 @@
 | 
				
			|||||||
package org.dromara.web.controller;
 | 
					package org.dromara.web.controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.annotation.SaIgnore;
 | 
					import cn.dev33.satoken.annotation.SaIgnore;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.exception.NotLoginException;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.codec.Base64;
 | 
				
			||||||
import cn.hutool.core.collection.CollUtil;
 | 
					import cn.hutool.core.collection.CollUtil;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
import jakarta.servlet.http.HttpServletRequest;
 | 
					import jakarta.servlet.http.HttpServletRequest;
 | 
				
			||||||
@@ -10,21 +13,23 @@ import me.zhyd.oauth.model.AuthResponse;
 | 
				
			|||||||
import me.zhyd.oauth.model.AuthUser;
 | 
					import me.zhyd.oauth.model.AuthUser;
 | 
				
			||||||
import me.zhyd.oauth.request.AuthRequest;
 | 
					import me.zhyd.oauth.request.AuthRequest;
 | 
				
			||||||
import me.zhyd.oauth.utils.AuthStateUtils;
 | 
					import me.zhyd.oauth.utils.AuthStateUtils;
 | 
				
			||||||
import org.dromara.common.core.constant.UserConstants;
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.R;
 | 
					import org.dromara.common.core.domain.R;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginBody;
 | 
					import org.dromara.common.core.domain.model.LoginBody;
 | 
				
			||||||
import org.dromara.common.core.domain.model.RegisterBody;
 | 
					import org.dromara.common.core.domain.model.RegisterBody;
 | 
				
			||||||
import org.dromara.common.core.domain.model.SocialLoginBody;
 | 
					import org.dromara.common.core.domain.model.SocialLoginBody;
 | 
				
			||||||
import org.dromara.common.core.utils.*;
 | 
					import org.dromara.common.core.utils.*;
 | 
				
			||||||
 | 
					import org.dromara.common.encrypt.annotation.ApiEncrypt;
 | 
				
			||||||
import org.dromara.common.json.utils.JsonUtils;
 | 
					import org.dromara.common.json.utils.JsonUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
 | 
					import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
 | 
				
			||||||
import org.dromara.common.social.config.properties.SocialProperties;
 | 
					import org.dromara.common.social.config.properties.SocialProperties;
 | 
				
			||||||
import org.dromara.common.social.utils.SocialUtils;
 | 
					import org.dromara.common.social.utils.SocialUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.sse.dto.SseMessageDto;
 | 
				
			||||||
 | 
					import org.dromara.common.sse.utils.SseMessageUtils;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.common.websocket.utils.WebSocketUtils;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.bo.SysTenantBo;
 | 
					import org.dromara.system.domain.bo.SysTenantBo;
 | 
				
			||||||
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysTenantVo;
 | 
					import org.dromara.system.domain.vo.SysTenantVo;
 | 
				
			||||||
import org.dromara.system.service.ISysClientService;
 | 
					import org.dromara.system.service.ISysClientService;
 | 
				
			||||||
import org.dromara.system.service.ISysConfigService;
 | 
					import org.dromara.system.service.ISysConfigService;
 | 
				
			||||||
@@ -40,7 +45,10 @@ import org.springframework.validation.annotation.Validated;
 | 
				
			|||||||
import org.springframework.web.bind.annotation.*;
 | 
					import org.springframework.web.bind.annotation.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.net.URL;
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.nio.charset.StandardCharsets;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.concurrent.ScheduledExecutorService;
 | 
					import java.util.concurrent.ScheduledExecutorService;
 | 
				
			||||||
import java.util.concurrent.TimeUnit;
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,7 +59,6 @@ import java.util.concurrent.TimeUnit;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
@Slf4j
 | 
					@Slf4j
 | 
				
			||||||
@SaIgnore
 | 
					@SaIgnore
 | 
				
			||||||
@Validated
 | 
					 | 
				
			||||||
@RequiredArgsConstructor
 | 
					@RequiredArgsConstructor
 | 
				
			||||||
@RestController
 | 
					@RestController
 | 
				
			||||||
@RequestMapping("/auth")
 | 
					@RequestMapping("/auth")
 | 
				
			||||||
@@ -73,19 +80,20 @@ public class AuthController {
 | 
				
			|||||||
     * @param body 登录信息
 | 
					     * @param body 登录信息
 | 
				
			||||||
     * @return 结果
 | 
					     * @return 结果
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @ApiEncrypt
 | 
				
			||||||
    @PostMapping("/login")
 | 
					    @PostMapping("/login")
 | 
				
			||||||
    public R<LoginVo> login(@Validated @RequestBody String body) {
 | 
					    public R<LoginVo> login(@RequestBody String body) {
 | 
				
			||||||
        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
 | 
					        LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        // 授权类型和客户端id
 | 
					        // 授权类型和客户端id
 | 
				
			||||||
        String clientId = loginBody.getClientId();
 | 
					        String clientId = loginBody.getClientId();
 | 
				
			||||||
        String grantType = loginBody.getGrantType();
 | 
					        String grantType = loginBody.getGrantType();
 | 
				
			||||||
        SysClient client = clientService.queryByClientId(clientId);
 | 
					        SysClientVo client = clientService.queryByClientId(clientId);
 | 
				
			||||||
        // 查询不到 client 或 client 内不包含 grantType
 | 
					        // 查询不到 client 或 client 内不包含 grantType
 | 
				
			||||||
        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
 | 
					        if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
 | 
				
			||||||
            log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
 | 
					            log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
 | 
				
			||||||
            return R.fail(MessageUtils.message("auth.grant.type.error"));
 | 
					            return R.fail(MessageUtils.message("auth.grant.type.error"));
 | 
				
			||||||
        } else if (!UserConstants.NORMAL.equals(client.getStatus())) {
 | 
					        } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
 | 
				
			||||||
            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
 | 
					            return R.fail(MessageUtils.message("auth.grant.type.blocked"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // 校验租户
 | 
					        // 校验租户
 | 
				
			||||||
@@ -95,36 +103,46 @@ public class AuthController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        Long userId = LoginHelper.getUserId();
 | 
					        Long userId = LoginHelper.getUserId();
 | 
				
			||||||
        scheduledExecutorService.schedule(() -> {
 | 
					        scheduledExecutorService.schedule(() -> {
 | 
				
			||||||
            WebSocketUtils.sendMessage(userId, "欢迎登录RuoYi-Vue-Plus后台管理系统");
 | 
					            SseMessageDto dto = new SseMessageDto();
 | 
				
			||||||
        }, 3, TimeUnit.SECONDS);
 | 
					            dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
 | 
				
			||||||
 | 
					            dto.setUserIds(List.of(userId));
 | 
				
			||||||
 | 
					            SseMessageUtils.publishMessage(dto);
 | 
				
			||||||
 | 
					        }, 5, TimeUnit.SECONDS);
 | 
				
			||||||
        return R.ok(loginVo);
 | 
					        return R.ok(loginVo);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 第三方登录请求
 | 
					     * 获取跳转URL
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param source 登录来源
 | 
					     * @param source 登录来源
 | 
				
			||||||
     * @return 结果
 | 
					     * @return 结果
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @GetMapping("/binding/{source}")
 | 
					    @GetMapping("/binding/{source}")
 | 
				
			||||||
    public R<String> authBinding(@PathVariable("source") String source) {
 | 
					    public R<String> authBinding(@PathVariable("source") String source,
 | 
				
			||||||
 | 
					                                 @RequestParam String tenantId, @RequestParam String domain) {
 | 
				
			||||||
        SocialLoginConfigProperties obj = socialProperties.getType().get(source);
 | 
					        SocialLoginConfigProperties obj = socialProperties.getType().get(source);
 | 
				
			||||||
        if (ObjectUtil.isNull(obj)) {
 | 
					        if (ObjectUtil.isNull(obj)) {
 | 
				
			||||||
            return R.fail(source + "平台账号暂不支持");
 | 
					            return R.fail(source + "平台账号暂不支持");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
 | 
					        AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
 | 
				
			||||||
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
 | 
					        Map<String, String> map = new HashMap<>();
 | 
				
			||||||
 | 
					        map.put("tenantId", tenantId);
 | 
				
			||||||
 | 
					        map.put("domain", domain);
 | 
				
			||||||
 | 
					        map.put("state", AuthStateUtils.createState());
 | 
				
			||||||
 | 
					        String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
 | 
				
			||||||
        return R.ok("操作成功", authorizeUrl);
 | 
					        return R.ok("操作成功", authorizeUrl);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 第三方登录回调业务处理 绑定授权
 | 
					     * 前端回调绑定授权(需要token)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param loginBody 请求体
 | 
					     * @param loginBody 请求体
 | 
				
			||||||
     * @return 结果
 | 
					     * @return 结果
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @PostMapping("/social/callback")
 | 
					    @PostMapping("/social/callback")
 | 
				
			||||||
    public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
 | 
					    public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
 | 
				
			||||||
 | 
					        // 校验token
 | 
				
			||||||
 | 
					        StpUtil.checkLogin();
 | 
				
			||||||
        // 获取第三方登录信息
 | 
					        // 获取第三方登录信息
 | 
				
			||||||
        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
 | 
					        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
 | 
				
			||||||
                loginBody.getSource(), loginBody.getSocialCode(),
 | 
					                loginBody.getSource(), loginBody.getSocialCode(),
 | 
				
			||||||
@@ -140,12 +158,14 @@ public class AuthController {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 取消授权
 | 
					     * 取消授权(需要token)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param socialId socialId
 | 
					     * @param socialId socialId
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @DeleteMapping(value = "/unlock/{socialId}")
 | 
					    @DeleteMapping(value = "/unlock/{socialId}")
 | 
				
			||||||
    public R<Void> unlockSocial(@PathVariable Long socialId) {
 | 
					    public R<Void> unlockSocial(@PathVariable Long socialId) {
 | 
				
			||||||
 | 
					        // 校验token
 | 
				
			||||||
 | 
					        StpUtil.checkLogin();
 | 
				
			||||||
        Boolean rows = socialUserService.deleteWithValidById(socialId);
 | 
					        Boolean rows = socialUserService.deleteWithValidById(socialId);
 | 
				
			||||||
        return rows ? R.ok() : R.fail("取消授权失败");
 | 
					        return rows ? R.ok() : R.fail("取消授权失败");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -163,6 +183,7 @@ public class AuthController {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 用户注册
 | 
					     * 用户注册
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @ApiEncrypt
 | 
				
			||||||
    @PostMapping("/register")
 | 
					    @PostMapping("/register")
 | 
				
			||||||
    public R<Void> register(@Validated @RequestBody RegisterBody user) {
 | 
					    public R<Void> register(@Validated @RequestBody RegisterBody user) {
 | 
				
			||||||
        if (!configService.selectRegisterEnabled(user.getTenantId())) {
 | 
					        if (!configService.selectRegisterEnabled(user.getTenantId())) {
 | 
				
			||||||
@@ -179,8 +200,26 @@ public class AuthController {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @GetMapping("/tenant/list")
 | 
					    @GetMapping("/tenant/list")
 | 
				
			||||||
    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
 | 
					    public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
 | 
				
			||||||
 | 
					        // 返回对象
 | 
				
			||||||
 | 
					        LoginTenantVo result = new LoginTenantVo();
 | 
				
			||||||
 | 
					        boolean enable = TenantHelper.isEnable();
 | 
				
			||||||
 | 
					        result.setTenantEnabled(enable);
 | 
				
			||||||
 | 
					        // 如果未开启租户这直接返回
 | 
				
			||||||
 | 
					        if (!enable) {
 | 
				
			||||||
 | 
					            return R.ok(result);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
 | 
					        List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
 | 
				
			||||||
        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
 | 
					        List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 如果只超管返回所有租户
 | 
				
			||||||
 | 
					            if (LoginHelper.isSuperAdmin()) {
 | 
				
			||||||
 | 
					                result.setVoList(voList);
 | 
				
			||||||
 | 
					                return R.ok(result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (NotLoginException ignored) {
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 获取域名
 | 
					        // 获取域名
 | 
				
			||||||
        String host;
 | 
					        String host;
 | 
				
			||||||
        String referer = request.getHeader("referer");
 | 
					        String referer = request.getHeader("referer");
 | 
				
			||||||
@@ -192,12 +231,9 @@ public class AuthController {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        // 根据域名进行筛选
 | 
					        // 根据域名进行筛选
 | 
				
			||||||
        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
 | 
					        List<TenantListVo> list = StreamUtils.filter(voList, vo ->
 | 
				
			||||||
                StringUtils.equals(vo.getDomain(), host));
 | 
					            StringUtils.equalsIgnoreCase(vo.getDomain(), host));
 | 
				
			||||||
        // 返回对象
 | 
					        result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
 | 
				
			||||||
        LoginTenantVo vo = new LoginTenantVo();
 | 
					        return R.ok(result);
 | 
				
			||||||
        vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
 | 
					 | 
				
			||||||
        vo.setTenantEnabled(TenantHelper.isEnable());
 | 
					 | 
				
			||||||
        return R.ok(vo);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,25 +5,27 @@ import cn.hutool.captcha.AbstractCaptcha;
 | 
				
			|||||||
import cn.hutool.captcha.generator.CodeGenerator;
 | 
					import cn.hutool.captcha.generator.CodeGenerator;
 | 
				
			||||||
import cn.hutool.core.util.IdUtil;
 | 
					import cn.hutool.core.util.IdUtil;
 | 
				
			||||||
import cn.hutool.core.util.RandomUtil;
 | 
					import cn.hutool.core.util.RandomUtil;
 | 
				
			||||||
 | 
					import jakarta.validation.constraints.NotBlank;
 | 
				
			||||||
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.GlobalConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.R;
 | 
					import org.dromara.common.core.domain.R;
 | 
				
			||||||
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
import org.dromara.common.core.utils.SpringUtils;
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
import org.dromara.common.core.utils.StringUtils;
 | 
					import org.dromara.common.core.utils.StringUtils;
 | 
				
			||||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
					import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
				
			||||||
import org.dromara.common.mail.config.properties.MailProperties;
 | 
					import org.dromara.common.mail.config.properties.MailProperties;
 | 
				
			||||||
import org.dromara.common.mail.utils.MailUtils;
 | 
					import org.dromara.common.mail.utils.MailUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.ratelimiter.annotation.RateLimiter;
 | 
				
			||||||
 | 
					import org.dromara.common.ratelimiter.enums.LimitType;
 | 
				
			||||||
import org.dromara.common.redis.utils.RedisUtils;
 | 
					import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			||||||
import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
					import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
				
			||||||
import org.dromara.common.web.enums.CaptchaType;
 | 
					import org.dromara.common.web.enums.CaptchaType;
 | 
				
			||||||
import org.dromara.sms4j.api.SmsBlend;
 | 
					import org.dromara.sms4j.api.SmsBlend;
 | 
				
			||||||
import org.dromara.sms4j.api.entity.SmsResponse;
 | 
					import org.dromara.sms4j.api.entity.SmsResponse;
 | 
				
			||||||
import org.dromara.sms4j.core.factory.SmsFactory;
 | 
					import org.dromara.sms4j.core.factory.SmsFactory;
 | 
				
			||||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
 | 
					 | 
				
			||||||
import org.dromara.web.domain.vo.CaptchaVo;
 | 
					import org.dromara.web.domain.vo.CaptchaVo;
 | 
				
			||||||
import jakarta.validation.constraints.NotBlank;
 | 
					 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					 | 
				
			||||||
import org.springframework.expression.Expression;
 | 
					import org.springframework.expression.Expression;
 | 
				
			||||||
import org.springframework.expression.ExpressionParser;
 | 
					import org.springframework.expression.ExpressionParser;
 | 
				
			||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
 | 
					import org.springframework.expression.spel.standard.SpelExpressionParser;
 | 
				
			||||||
@@ -54,6 +56,7 @@ public class CaptchaController {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param phonenumber 用户手机号
 | 
					     * @param phonenumber 用户手机号
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @RateLimiter(key = "#phonenumber", time = 60, count = 1)
 | 
				
			||||||
    @GetMapping("/resource/sms/code")
 | 
					    @GetMapping("/resource/sms/code")
 | 
				
			||||||
    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
 | 
					    public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
 | 
				
			||||||
        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
 | 
					        String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
 | 
				
			||||||
@@ -63,11 +66,11 @@ public class CaptchaController {
 | 
				
			|||||||
        String templateId = "";
 | 
					        String templateId = "";
 | 
				
			||||||
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
 | 
					        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
 | 
				
			||||||
        map.put("code", code);
 | 
					        map.put("code", code);
 | 
				
			||||||
        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
 | 
					        SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
 | 
				
			||||||
        SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
 | 
					        SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
 | 
				
			||||||
        if (!"OK".equals(smsResponse.getCode())) {
 | 
					        if (!smsResponse.isSuccess()) {
 | 
				
			||||||
            log.error("验证码短信发送异常 => {}", smsResponse);
 | 
					            log.error("验证码短信发送异常 => {}", smsResponse);
 | 
				
			||||||
            return R.fail(smsResponse.getMessage());
 | 
					            return R.fail(smsResponse.getData().toString());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return R.ok();
 | 
					        return R.ok();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -82,6 +85,16 @@ public class CaptchaController {
 | 
				
			|||||||
        if (!mailProperties.getEnabled()) {
 | 
					        if (!mailProperties.getEnabled()) {
 | 
				
			||||||
            return R.fail("当前系统没有开启邮箱功能!");
 | 
					            return R.fail("当前系统没有开启邮箱功能!");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        SpringUtils.getAopProxy(this).emailCodeImpl(email);
 | 
				
			||||||
 | 
					        return R.ok();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 邮箱验证码
 | 
				
			||||||
 | 
					     * 独立方法避免验证码关闭之后仍然走限流
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @RateLimiter(key = "#email", time = 60, count = 1)
 | 
				
			||||||
 | 
					    public void emailCodeImpl(String email) {
 | 
				
			||||||
        String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
 | 
					        String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
 | 
				
			||||||
        String code = RandomUtil.randomNumbers(4);
 | 
					        String code = RandomUtil.randomNumbers(4);
 | 
				
			||||||
        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
					        RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
				
			||||||
@@ -89,9 +102,8 @@ public class CaptchaController {
 | 
				
			|||||||
            MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
 | 
					            MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            log.error("验证码短信发送异常 => {}", e.getMessage());
 | 
					            log.error("验证码短信发送异常 => {}", e.getMessage());
 | 
				
			||||||
            return R.fail(e.getMessage());
 | 
					            throw new ServiceException(e.getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return R.ok();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -99,12 +111,21 @@ public class CaptchaController {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @GetMapping("/auth/code")
 | 
					    @GetMapping("/auth/code")
 | 
				
			||||||
    public R<CaptchaVo> getCode() {
 | 
					    public R<CaptchaVo> getCode() {
 | 
				
			||||||
        CaptchaVo captchaVo = new CaptchaVo();
 | 
					 | 
				
			||||||
        boolean captchaEnabled = captchaProperties.getEnable();
 | 
					        boolean captchaEnabled = captchaProperties.getEnable();
 | 
				
			||||||
        if (!captchaEnabled) {
 | 
					        if (!captchaEnabled) {
 | 
				
			||||||
 | 
					            CaptchaVo captchaVo = new CaptchaVo();
 | 
				
			||||||
            captchaVo.setCaptchaEnabled(false);
 | 
					            captchaVo.setCaptchaEnabled(false);
 | 
				
			||||||
            return R.ok(captchaVo);
 | 
					            return R.ok(captchaVo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 生成验证码
 | 
				
			||||||
 | 
					     * 独立方法避免验证码关闭之后仍然走限流
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
 | 
				
			||||||
 | 
					    public CaptchaVo getCodeImpl() {
 | 
				
			||||||
        // 保存验证码信息
 | 
					        // 保存验证码信息
 | 
				
			||||||
        String uuid = IdUtil.simpleUUID();
 | 
					        String uuid = IdUtil.simpleUUID();
 | 
				
			||||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
 | 
					        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
 | 
				
			||||||
@@ -116,6 +137,7 @@ public class CaptchaController {
 | 
				
			|||||||
        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
 | 
					        AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
 | 
				
			||||||
        captcha.setGenerator(codeGenerator);
 | 
					        captcha.setGenerator(codeGenerator);
 | 
				
			||||||
        captcha.createCode();
 | 
					        captcha.createCode();
 | 
				
			||||||
 | 
					        // 如果是数学验证码,使用SpEL表达式处理验证码结果
 | 
				
			||||||
        String code = captcha.getCode();
 | 
					        String code = captcha.getCode();
 | 
				
			||||||
        if (isMath) {
 | 
					        if (isMath) {
 | 
				
			||||||
            ExpressionParser parser = new SpelExpressionParser();
 | 
					            ExpressionParser parser = new SpelExpressionParser();
 | 
				
			||||||
@@ -123,9 +145,10 @@ public class CaptchaController {
 | 
				
			|||||||
            code = exp.getValue(String.class);
 | 
					            code = exp.getValue(String.class);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
					        RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
 | 
				
			||||||
 | 
					        CaptchaVo captchaVo = new CaptchaVo();
 | 
				
			||||||
        captchaVo.setUuid(uuid);
 | 
					        captchaVo.setUuid(uuid);
 | 
				
			||||||
        captchaVo.setImg(captcha.getImageBase64());
 | 
					        captchaVo.setImg(captcha.getImageBase64());
 | 
				
			||||||
        return R.ok(captchaVo);
 | 
					        return captchaVo;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
package org.dromara.web.controller;
 | 
					package org.dromara.web.controller;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.annotation.SaIgnore;
 | 
					import cn.dev33.satoken.annotation.SaIgnore;
 | 
				
			||||||
import org.dromara.common.core.config.RuoYiConfig;
 | 
					 | 
				
			||||||
import org.dromara.common.core.utils.StringUtils;
 | 
					 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.StringUtils;
 | 
				
			||||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
					import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
import org.springframework.web.bind.annotation.RestController;
 | 
					import org.springframework.web.bind.annotation.RestController;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,16 +17,12 @@ import org.springframework.web.bind.annotation.RestController;
 | 
				
			|||||||
@RestController
 | 
					@RestController
 | 
				
			||||||
public class IndexController {
 | 
					public class IndexController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 系统基础配置
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private final RuoYiConfig ruoyiConfig;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 访问首页,提示语
 | 
					     * 访问首页,提示语
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @GetMapping("/")
 | 
					    @GetMapping("/")
 | 
				
			||||||
    public String index() {
 | 
					    public String index() {
 | 
				
			||||||
        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
 | 
					        return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,10 +13,19 @@ import lombok.Data;
 | 
				
			|||||||
@AutoMapper(target = SysTenantVo.class)
 | 
					@AutoMapper(target = SysTenantVo.class)
 | 
				
			||||||
public class TenantListVo {
 | 
					public class TenantListVo {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 租户编号
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private String tenantId;
 | 
					    private String tenantId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 企业名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private String companyName;
 | 
					    private String companyName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 域名
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private String domain;
 | 
					    private String domain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,163 @@
 | 
				
			|||||||
 | 
					package org.dromara.web.listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.dev33.satoken.listener.SaTokenListener;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
 | 
					import cn.hutool.core.convert.Convert;
 | 
				
			||||||
 | 
					import cn.hutool.http.useragent.UserAgent;
 | 
				
			||||||
 | 
					import cn.hutool.http.useragent.UserAgentUtil;
 | 
				
			||||||
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.CacheConstants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.UserOnlineDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.MessageUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.ServletUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.ip.AddressUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.log.event.LogininforEvent;
 | 
				
			||||||
 | 
					import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
 | 
					import org.dromara.web.service.SysLoginService;
 | 
				
			||||||
 | 
					import org.springframework.stereotype.Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.time.Duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 用户行为 侦听器的实现
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Lion Li
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@RequiredArgsConstructor
 | 
				
			||||||
 | 
					@Component
 | 
				
			||||||
 | 
					@Slf4j
 | 
				
			||||||
 | 
					public class UserActionListener implements SaTokenListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final SysLoginService loginService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次登录时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
 | 
				
			||||||
 | 
					        UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
 | 
				
			||||||
 | 
					        String ip = ServletUtils.getClientIP();
 | 
				
			||||||
 | 
					        UserOnlineDTO dto = new UserOnlineDTO();
 | 
				
			||||||
 | 
					        dto.setIpaddr(ip);
 | 
				
			||||||
 | 
					        dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
 | 
				
			||||||
 | 
					        dto.setBrowser(userAgent.getBrowser().getName());
 | 
				
			||||||
 | 
					        dto.setOs(userAgent.getOs().getName());
 | 
				
			||||||
 | 
					        dto.setLoginTime(System.currentTimeMillis());
 | 
				
			||||||
 | 
					        dto.setTokenId(tokenValue);
 | 
				
			||||||
 | 
					        String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
 | 
				
			||||||
 | 
					        String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
 | 
				
			||||||
 | 
					        dto.setUserName(username);
 | 
				
			||||||
 | 
					        dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
 | 
				
			||||||
 | 
					        dto.setDeviceType(loginParameter.getDeviceType());
 | 
				
			||||||
 | 
					        dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
 | 
				
			||||||
 | 
					        TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
 | 
					            if(loginParameter.getTimeout() == -1) {
 | 
				
			||||||
 | 
					                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        // 记录登录日志
 | 
				
			||||||
 | 
					        LogininforEvent logininforEvent = new LogininforEvent();
 | 
				
			||||||
 | 
					        logininforEvent.setTenantId(tenantId);
 | 
				
			||||||
 | 
					        logininforEvent.setUsername(username);
 | 
				
			||||||
 | 
					        logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
 | 
				
			||||||
 | 
					        logininforEvent.setMessage(MessageUtils.message("user.login.success"));
 | 
				
			||||||
 | 
					        logininforEvent.setRequest(ServletUtils.getRequest());
 | 
				
			||||||
 | 
					        SpringUtils.context().publishEvent(logininforEvent);
 | 
				
			||||||
 | 
					        // 更新登录信息
 | 
				
			||||||
 | 
					        loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
 | 
				
			||||||
 | 
					        log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次注销时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doLogout(String loginType, Object loginId, String tokenValue) {
 | 
				
			||||||
 | 
					        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
 | 
				
			||||||
 | 
					        TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
 | 
					            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次被踢下线时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doKickout(String loginType, Object loginId, String tokenValue) {
 | 
				
			||||||
 | 
					        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
 | 
				
			||||||
 | 
					        TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
 | 
					            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次被顶下线时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doReplaced(String loginType, Object loginId, String tokenValue) {
 | 
				
			||||||
 | 
					        String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
 | 
				
			||||||
 | 
					        TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
 | 
					            RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次被封禁时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次被解封时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doUntieDisable(String loginType, Object loginId, String service) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次打开二级认证时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次创建Session时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doCloseSafe(String loginType, String tokenValue, String service) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次创建Session时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doCreateSession(String id) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次注销Session时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doLogoutSession(String id) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每次Token续期时触发
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,6 +4,7 @@ package org.dromara.web.service;
 | 
				
			|||||||
import org.dromara.common.core.exception.ServiceException;
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
import org.dromara.common.core.utils.SpringUtils;
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					import org.dromara.system.domain.SysClient;
 | 
				
			||||||
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.web.domain.vo.LoginVo;
 | 
					import org.dromara.web.domain.vo.LoginVo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -17,8 +18,13 @@ public interface IAuthStrategy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 登录
 | 
					     * 登录
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param body      登录对象
 | 
				
			||||||
 | 
					     * @param client    授权管理视图对象
 | 
				
			||||||
 | 
					     * @param grantType 授权类型
 | 
				
			||||||
 | 
					     * @return 登录验证信息
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    static LoginVo login(String body, SysClient client, String grantType) {
 | 
					    static LoginVo login(String body, SysClientVo client, String grantType) {
 | 
				
			||||||
        // 授权类型和客户端id
 | 
					        // 授权类型和客户端id
 | 
				
			||||||
        String beanName = grantType + BASE_NAME;
 | 
					        String beanName = grantType + BASE_NAME;
 | 
				
			||||||
        if (!SpringUtils.containsBean(beanName)) {
 | 
					        if (!SpringUtils.containsBean(beanName)) {
 | 
				
			||||||
@@ -30,7 +36,11 @@ public interface IAuthStrategy {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 登录
 | 
					     * 登录
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param body   登录对象
 | 
				
			||||||
 | 
					     * @param client 授权管理视图对象
 | 
				
			||||||
 | 
					     * @return 登录验证信息
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    LoginVo login(String body, SysClient client);
 | 
					    LoginVo login(String body, SysClientVo client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,33 +3,35 @@ package org.dromara.web.service;
 | 
				
			|||||||
import cn.dev33.satoken.exception.NotLoginException;
 | 
					import cn.dev33.satoken.exception.NotLoginException;
 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
import cn.hutool.core.bean.BeanUtil;
 | 
					import cn.hutool.core.bean.BeanUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.collection.CollUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.lang.Opt;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
 | 
					import com.baomidou.lock.annotation.Lock4j;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import me.zhyd.oauth.model.AuthUser;
 | 
					import me.zhyd.oauth.model.AuthUser;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.CacheConstants;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.constant.TenantConstants;
 | 
					import org.dromara.common.core.constant.TenantConstants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.PostDTO;
 | 
				
			||||||
import org.dromara.common.core.domain.dto.RoleDTO;
 | 
					import org.dromara.common.core.domain.dto.RoleDTO;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
					import org.dromara.common.core.domain.model.LoginUser;
 | 
				
			||||||
import org.dromara.common.core.enums.LoginType;
 | 
					import org.dromara.common.core.enums.LoginType;
 | 
				
			||||||
import org.dromara.common.core.enums.TenantStatus;
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.UserException;
 | 
					import org.dromara.common.core.exception.user.UserException;
 | 
				
			||||||
import org.dromara.common.core.utils.*;
 | 
					import org.dromara.common.core.utils.*;
 | 
				
			||||||
import org.dromara.common.log.event.LogininforEvent;
 | 
					import org.dromara.common.log.event.LogininforEvent;
 | 
				
			||||||
 | 
					import org.dromara.common.mybatis.helper.DataPermissionHelper;
 | 
				
			||||||
import org.dromara.common.redis.utils.RedisUtils;
 | 
					import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.tenant.exception.TenantException;
 | 
					import org.dromara.common.tenant.exception.TenantException;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.system.domain.SysUser;
 | 
					import org.dromara.system.domain.SysUser;
 | 
				
			||||||
import org.dromara.system.domain.bo.SysSocialBo;
 | 
					import org.dromara.system.domain.bo.SysSocialBo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysSocialVo;
 | 
					import org.dromara.system.domain.vo.*;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysTenantVo;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
import org.dromara.system.service.ISysPermissionService;
 | 
					import org.dromara.system.service.*;
 | 
				
			||||||
import org.dromara.system.service.ISysSocialService;
 | 
					 | 
				
			||||||
import org.dromara.system.service.ISysTenantService;
 | 
					 | 
				
			||||||
import org.springframework.beans.factory.annotation.Value;
 | 
					import org.springframework.beans.factory.annotation.Value;
 | 
				
			||||||
import org.springframework.stereotype.Service;
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,6 +59,9 @@ public class SysLoginService {
 | 
				
			|||||||
    private final ISysTenantService tenantService;
 | 
					    private final ISysTenantService tenantService;
 | 
				
			||||||
    private final ISysPermissionService permissionService;
 | 
					    private final ISysPermissionService permissionService;
 | 
				
			||||||
    private final ISysSocialService sysSocialService;
 | 
					    private final ISysSocialService sysSocialService;
 | 
				
			||||||
 | 
					    private final ISysRoleService roleService;
 | 
				
			||||||
 | 
					    private final ISysDeptService deptService;
 | 
				
			||||||
 | 
					    private final ISysPostService postService;
 | 
				
			||||||
    private final SysUserMapper userMapper;
 | 
					    private final SysUserMapper userMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -64,27 +69,37 @@ public class SysLoginService {
 | 
				
			|||||||
     * 绑定第三方用户
 | 
					     * 绑定第三方用户
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param authUserData 授权响应实体
 | 
					     * @param authUserData 授权响应实体
 | 
				
			||||||
     * @return 统一响应实体
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @Lock4j
 | 
				
			||||||
    public void socialRegister(AuthUser authUserData) {
 | 
					    public void socialRegister(AuthUser authUserData) {
 | 
				
			||||||
        String authId = authUserData.getSource() + authUserData.getUuid();
 | 
					        String authId = authUserData.getSource() + authUserData.getUuid();
 | 
				
			||||||
        // 第三方用户信息
 | 
					        // 第三方用户信息
 | 
				
			||||||
        SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
 | 
					        SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
 | 
				
			||||||
        BeanUtil.copyProperties(authUserData.getToken(), bo);
 | 
					        BeanUtil.copyProperties(authUserData.getToken(), bo);
 | 
				
			||||||
        bo.setUserId(LoginHelper.getUserId());
 | 
					        Long userId = LoginHelper.getUserId();
 | 
				
			||||||
 | 
					        bo.setUserId(userId);
 | 
				
			||||||
        bo.setAuthId(authId);
 | 
					        bo.setAuthId(authId);
 | 
				
			||||||
        bo.setOpenId(authUserData.getUuid());
 | 
					        bo.setOpenId(authUserData.getUuid());
 | 
				
			||||||
        bo.setUserName(authUserData.getUsername());
 | 
					        bo.setUserName(authUserData.getUsername());
 | 
				
			||||||
        bo.setNickName(authUserData.getNickname());
 | 
					        bo.setNickName(authUserData.getNickname());
 | 
				
			||||||
 | 
					        List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId);
 | 
				
			||||||
 | 
					        if (CollUtil.isNotEmpty(checkList)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("此三方账号已经被绑定!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        // 查询是否已经绑定用户
 | 
					        // 查询是否已经绑定用户
 | 
				
			||||||
        SysSocialVo vo = sysSocialService.selectByAuthId(authId);
 | 
					        SysSocialBo params = new SysSocialBo();
 | 
				
			||||||
        if (ObjectUtil.isEmpty(vo)) {
 | 
					        params.setUserId(userId);
 | 
				
			||||||
 | 
					        params.setSource(bo.getSource());
 | 
				
			||||||
 | 
					        List<SysSocialVo> list = sysSocialService.queryList(params);
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(list)) {
 | 
				
			||||||
            // 没有绑定用户, 新增用户信息
 | 
					            // 没有绑定用户, 新增用户信息
 | 
				
			||||||
            sysSocialService.insertByBo(bo);
 | 
					            sysSocialService.insertByBo(bo);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // 更新用户信息
 | 
					            // 更新用户信息
 | 
				
			||||||
            bo.setId(vo.getId());
 | 
					            bo.setId(list.get(0).getId());
 | 
				
			||||||
            sysSocialService.updateByBo(bo);
 | 
					            sysSocialService.updateByBo(bo);
 | 
				
			||||||
 | 
					            // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
 | 
				
			||||||
 | 
					            // throw new ServiceException("此平台账号已经被绑定!");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,6 +110,9 @@ public class SysLoginService {
 | 
				
			|||||||
    public void logout() {
 | 
					    public void logout() {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
					            LoginUser loginUser = LoginHelper.getLoginUser();
 | 
				
			||||||
 | 
					            if (ObjectUtil.isNull(loginUser)) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
 | 
					            if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
 | 
				
			||||||
                // 超级管理员 登出清除动态租户
 | 
					                // 超级管理员 登出清除动态租户
 | 
				
			||||||
                TenantHelper.clearDynamic();
 | 
					                TenantHelper.clearDynamic();
 | 
				
			||||||
@@ -127,23 +145,29 @@ public class SysLoginService {
 | 
				
			|||||||
        SpringUtils.context().publishEvent(logininforEvent);
 | 
					        SpringUtils.context().publishEvent(logininforEvent);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 构建登录用户
 | 
					     * 构建登录用户
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public LoginUser buildLoginUser(SysUserVo user) {
 | 
					    public LoginUser buildLoginUser(SysUserVo user) {
 | 
				
			||||||
        LoginUser loginUser = new LoginUser();
 | 
					        LoginUser loginUser = new LoginUser();
 | 
				
			||||||
 | 
					        Long userId = user.getUserId();
 | 
				
			||||||
        loginUser.setTenantId(user.getTenantId());
 | 
					        loginUser.setTenantId(user.getTenantId());
 | 
				
			||||||
        loginUser.setUserId(user.getUserId());
 | 
					        loginUser.setUserId(userId);
 | 
				
			||||||
        loginUser.setDeptId(user.getDeptId());
 | 
					        loginUser.setDeptId(user.getDeptId());
 | 
				
			||||||
        loginUser.setUsername(user.getUserName());
 | 
					        loginUser.setUsername(user.getUserName());
 | 
				
			||||||
        loginUser.setNickname(user.getNickName());
 | 
					        loginUser.setNickname(user.getNickName());
 | 
				
			||||||
        loginUser.setUserType(user.getUserType());
 | 
					        loginUser.setUserType(user.getUserType());
 | 
				
			||||||
        loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
 | 
					        loginUser.setMenuPermission(permissionService.getMenuPermission(userId));
 | 
				
			||||||
        loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
 | 
					        loginUser.setRolePermission(permissionService.getRolePermission(userId));
 | 
				
			||||||
        loginUser.setDeptName(ObjectUtil.isNull(user.getDept()) ? "" : user.getDept().getDeptName());
 | 
					        if (ObjectUtil.isNotNull(user.getDeptId())) {
 | 
				
			||||||
        List<RoleDTO> roles = BeanUtil.copyToList(user.getRoles(), RoleDTO.class);
 | 
					            Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
 | 
				
			||||||
        loginUser.setRoles(roles);
 | 
					            loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
 | 
				
			||||||
 | 
					            loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
 | 
				
			||||||
 | 
					        List<SysPostVo> posts = postService.selectPostsByUserId(userId);
 | 
				
			||||||
 | 
					        loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
 | 
				
			||||||
 | 
					        loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
 | 
				
			||||||
        return loginUser;
 | 
					        return loginUser;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -152,20 +176,20 @@ public class SysLoginService {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param userId 用户ID
 | 
					     * @param userId 用户ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void recordLoginInfo(Long userId) {
 | 
					    public void recordLoginInfo(Long userId, String ip) {
 | 
				
			||||||
        SysUser sysUser = new SysUser();
 | 
					        SysUser sysUser = new SysUser();
 | 
				
			||||||
        sysUser.setUserId(userId);
 | 
					        sysUser.setUserId(userId);
 | 
				
			||||||
        sysUser.setLoginIp(ServletUtils.getClientIP());
 | 
					        sysUser.setLoginIp(ip);
 | 
				
			||||||
        sysUser.setLoginDate(DateUtils.getNowDate());
 | 
					        sysUser.setLoginDate(DateUtils.getNowDate());
 | 
				
			||||||
        sysUser.setUpdateBy(userId);
 | 
					        sysUser.setUpdateBy(userId);
 | 
				
			||||||
        userMapper.updateById(sysUser);
 | 
					        DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 登录校验
 | 
					     * 登录校验
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
 | 
					    public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
 | 
				
			||||||
        String errorKey = GlobalConstants.PWD_ERR_CNT_KEY + username;
 | 
					        String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
 | 
				
			||||||
        String loginFail = Constants.LOGIN_FAIL;
 | 
					        String loginFail = Constants.LOGIN_FAIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
 | 
					        // 获取用户登录错误次数,默认为0 (可自定义限制策略 例如: key + username + ip)
 | 
				
			||||||
@@ -204,17 +228,17 @@ public class SysLoginService {
 | 
				
			|||||||
        if (!TenantHelper.isEnable()) {
 | 
					        if (!TenantHelper.isEnable()) {
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (StringUtils.isBlank(tenantId)) {
 | 
					        if (StringUtils.isBlank(tenantId)) {
 | 
				
			||||||
            throw new TenantException("tenant.number.not.blank");
 | 
					            throw new TenantException("tenant.number.not.blank");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
 | 
					        SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
 | 
				
			||||||
        if (ObjectUtil.isNull(tenant)) {
 | 
					        if (ObjectUtil.isNull(tenant)) {
 | 
				
			||||||
            log.info("登录租户:{} 不存在.", tenantId);
 | 
					            log.info("登录租户:{} 不存在.", tenantId);
 | 
				
			||||||
            throw new TenantException("tenant.not.exists");
 | 
					            throw new TenantException("tenant.not.exists");
 | 
				
			||||||
        } else if (TenantStatus.DISABLE.getCode().equals(tenant.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
 | 
				
			||||||
            log.info("登录租户:{} 已被停用.", tenantId);
 | 
					            log.info("登录租户:{} 已被停用.", tenantId);
 | 
				
			||||||
            throw new TenantException("tenant.blocked");
 | 
					            throw new TenantException("tenant.blocked");
 | 
				
			||||||
        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
 | 
					        } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
package org.dromara.web.service;
 | 
					package org.dromara.web.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.secure.BCrypt;
 | 
					import cn.hutool.crypto.digest.BCrypt;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					 | 
				
			||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
					import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
				
			||||||
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.GlobalConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.RegisterBody;
 | 
					import org.dromara.common.core.domain.model.RegisterBody;
 | 
				
			||||||
@@ -22,7 +22,6 @@ import org.dromara.system.domain.SysUser;
 | 
				
			|||||||
import org.dromara.system.domain.bo.SysUserBo;
 | 
					import org.dromara.system.domain.bo.SysUserBo;
 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
import org.dromara.system.service.ISysUserService;
 | 
					import org.dromara.system.service.ISysUserService;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					 | 
				
			||||||
import org.springframework.stereotype.Service;
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -59,10 +58,10 @@ public class SysRegisterService {
 | 
				
			|||||||
        sysUser.setPassword(BCrypt.hashpw(password));
 | 
					        sysUser.setPassword(BCrypt.hashpw(password));
 | 
				
			||||||
        sysUser.setUserType(userType);
 | 
					        sysUser.setUserType(userType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        boolean exist = userMapper.exists(new LambdaQueryWrapper<SysUser>()
 | 
					        boolean exist = TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
            .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
					            return userMapper.exists(new LambdaQueryWrapper<SysUser>()
 | 
				
			||||||
            .eq(SysUser::getUserName, sysUser.getUserName())
 | 
					                .eq(SysUser::getUserName, sysUser.getUserName()));
 | 
				
			||||||
            .ne(ObjectUtil.isNotNull(sysUser.getUserId()), SysUser::getUserId, sysUser.getUserId()));
 | 
					        });
 | 
				
			||||||
        if (exist) {
 | 
					        if (exist) {
 | 
				
			||||||
            throw new UserException("user.register.save.error", username);
 | 
					            throw new UserException("user.register.save.error", username);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -81,15 +80,15 @@ public class SysRegisterService {
 | 
				
			|||||||
     * @param uuid     唯一标识
 | 
					     * @param uuid     唯一标识
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
 | 
					    public void validateCaptcha(String tenantId, String username, String code, String uuid) {
 | 
				
			||||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
					        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
 | 
				
			||||||
        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
					        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
				
			||||||
        RedisUtils.deleteObject(verifyKey);
 | 
					        RedisUtils.deleteObject(verifyKey);
 | 
				
			||||||
        if (captcha == null) {
 | 
					        if (captcha == null) {
 | 
				
			||||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.expire"));
 | 
					            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
 | 
				
			||||||
            throw new CaptchaExpireException();
 | 
					            throw new CaptchaExpireException();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!code.equalsIgnoreCase(captcha)) {
 | 
					        if (!code.equalsIgnoreCase(captcha)) {
 | 
				
			||||||
            recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.jcaptcha.error"));
 | 
					            recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
 | 
				
			||||||
            throw new CaptchaException();
 | 
					            throw new CaptchaException();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
package org.dromara.web.service.impl;
 | 
					package org.dromara.web.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
					import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.GlobalConstants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.EmailLoginBody;
 | 
					import org.dromara.common.core.domain.model.EmailLoginBody;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
					import org.dromara.common.core.domain.model.LoginUser;
 | 
				
			||||||
import org.dromara.common.core.enums.LoginType;
 | 
					import org.dromara.common.core.enums.LoginType;
 | 
				
			||||||
import org.dromara.common.core.enums.UserStatus;
 | 
					 | 
				
			||||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
					import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.UserException;
 | 
					import org.dromara.common.core.exception.user.UserException;
 | 
				
			||||||
import org.dromara.common.core.utils.MessageUtils;
 | 
					import org.dromara.common.core.utils.MessageUtils;
 | 
				
			||||||
@@ -21,8 +21,8 @@ import org.dromara.common.json.utils.JsonUtils;
 | 
				
			|||||||
import org.dromara.common.redis.utils.RedisUtils;
 | 
					import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.SysUser;
 | 
					import org.dromara.system.domain.SysUser;
 | 
				
			||||||
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					import org.dromara.system.domain.vo.SysUserVo;
 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
import org.dromara.web.domain.vo.LoginVo;
 | 
					import org.dromara.web.domain.vo.LoginVo;
 | 
				
			||||||
@@ -44,23 +44,22 @@ public class EmailAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
    private final SysUserMapper userMapper;
 | 
					    private final SysUserMapper userMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public LoginVo login(String body, SysClient client) {
 | 
					    public LoginVo login(String body, SysClientVo client) {
 | 
				
			||||||
        EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
 | 
					        EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        String tenantId = loginBody.getTenantId();
 | 
					        String tenantId = loginBody.getTenantId();
 | 
				
			||||||
        String email = loginBody.getEmail();
 | 
					        String email = loginBody.getEmail();
 | 
				
			||||||
        String emailCode = loginBody.getEmailCode();
 | 
					        String emailCode = loginBody.getEmailCode();
 | 
				
			||||||
 | 
					        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
        // 通过邮箱查找用户
 | 
					            SysUserVo user = loadUserByEmail(email);
 | 
				
			||||||
        SysUserVo user = loadUserByEmail(tenantId, email);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
 | 
					            loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
 | 
				
			||||||
            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
					            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
				
			||||||
        LoginUser loginUser = loginService.buildLoginUser(user);
 | 
					            return loginService.buildLoginUser(user);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        loginUser.setClientKey(client.getClientKey());
 | 
					        loginUser.setClientKey(client.getClientKey());
 | 
				
			||||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
					        loginUser.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        SaLoginModel model = new SaLoginModel();
 | 
					        SaLoginParameter model = new SaLoginParameter();
 | 
				
			||||||
        model.setDevice(client.getDeviceType());
 | 
					        model.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
					        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
				
			||||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
					        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
				
			||||||
        model.setTimeout(client.getTimeout());
 | 
					        model.setTimeout(client.getTimeout());
 | 
				
			||||||
@@ -69,9 +68,6 @@ public class EmailAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 生成token
 | 
					        // 生成token
 | 
				
			||||||
        LoginHelper.login(loginUser, model);
 | 
					        LoginHelper.login(loginUser, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
					 | 
				
			||||||
        loginService.recordLoginInfo(user.getUserId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LoginVo loginVo = new LoginVo();
 | 
					        LoginVo loginVo = new LoginVo();
 | 
				
			||||||
        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
					        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
				
			||||||
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
					        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
				
			||||||
@@ -91,22 +87,16 @@ public class EmailAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        return code.equals(emailCode);
 | 
					        return code.equals(emailCode);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SysUserVo loadUserByEmail(String tenantId, String email) {
 | 
					    private SysUserVo loadUserByEmail(String email) {
 | 
				
			||||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
					        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
 | 
				
			||||||
            .select(SysUser::getEmail, SysUser::getStatus)
 | 
					 | 
				
			||||||
            .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
					 | 
				
			||||||
            .eq(SysUser::getEmail, email));
 | 
					 | 
				
			||||||
        if (ObjectUtil.isNull(user)) {
 | 
					        if (ObjectUtil.isNull(user)) {
 | 
				
			||||||
            log.info("登录用户:{} 不存在.", email);
 | 
					            log.info("登录用户:{} 不存在.", email);
 | 
				
			||||||
            throw new UserException("user.not.exists", email);
 | 
					            throw new UserException("user.not.exists", email);
 | 
				
			||||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
				
			||||||
            log.info("登录用户:{} 已被停用.", email);
 | 
					            log.info("登录用户:{} 已被停用.", email);
 | 
				
			||||||
            throw new UserException("user.blocked", email);
 | 
					            throw new UserException("user.blocked", email);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (TenantHelper.isEnable()) {
 | 
					        return user;
 | 
				
			||||||
            return userMapper.selectTenantUserByEmail(email, tenantId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return userMapper.selectUserByEmail(email);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,18 @@
 | 
				
			|||||||
package org.dromara.web.service.impl;
 | 
					package org.dromara.web.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.secure.BCrypt;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
 | 
					import cn.hutool.crypto.digest.BCrypt;
 | 
				
			||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
					import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.GlobalConstants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
					import org.dromara.common.core.domain.model.LoginUser;
 | 
				
			||||||
import org.dromara.common.core.domain.model.PasswordLoginBody;
 | 
					import org.dromara.common.core.domain.model.PasswordLoginBody;
 | 
				
			||||||
import org.dromara.common.core.enums.LoginType;
 | 
					import org.dromara.common.core.enums.LoginType;
 | 
				
			||||||
import org.dromara.common.core.enums.UserStatus;
 | 
					 | 
				
			||||||
import org.dromara.common.core.exception.user.CaptchaException;
 | 
					import org.dromara.common.core.exception.user.CaptchaException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
					import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.UserException;
 | 
					import org.dromara.common.core.exception.user.UserException;
 | 
				
			||||||
@@ -24,8 +24,8 @@ import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			|||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
					import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.SysUser;
 | 
					import org.dromara.system.domain.SysUser;
 | 
				
			||||||
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					import org.dromara.system.domain.vo.SysUserVo;
 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
import org.dromara.web.domain.vo.LoginVo;
 | 
					import org.dromara.web.domain.vo.LoginVo;
 | 
				
			||||||
@@ -48,7 +48,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
    private final SysUserMapper userMapper;
 | 
					    private final SysUserMapper userMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public LoginVo login(String body, SysClient client) {
 | 
					    public LoginVo login(String body, SysClientVo client) {
 | 
				
			||||||
        PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
 | 
					        PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        String tenantId = loginBody.getTenantId();
 | 
					        String tenantId = loginBody.getTenantId();
 | 
				
			||||||
@@ -62,15 +62,16 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        if (captchaEnabled) {
 | 
					        if (captchaEnabled) {
 | 
				
			||||||
            validateCaptcha(tenantId, username, code, uuid);
 | 
					            validateCaptcha(tenantId, username, code, uuid);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
        SysUserVo user = loadUserByUsername(tenantId, username);
 | 
					            SysUserVo user = loadUserByUsername(username);
 | 
				
			||||||
            loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
 | 
					            loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
 | 
				
			||||||
            // 此处可根据登录用户的数据不同 自行创建 loginUser
 | 
					            // 此处可根据登录用户的数据不同 自行创建 loginUser
 | 
				
			||||||
        LoginUser loginUser = loginService.buildLoginUser(user);
 | 
					            return loginService.buildLoginUser(user);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        loginUser.setClientKey(client.getClientKey());
 | 
					        loginUser.setClientKey(client.getClientKey());
 | 
				
			||||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
					        loginUser.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        SaLoginModel model = new SaLoginModel();
 | 
					        SaLoginParameter model = new SaLoginParameter();
 | 
				
			||||||
        model.setDevice(client.getDeviceType());
 | 
					        model.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
					        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
				
			||||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
					        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
				
			||||||
        model.setTimeout(client.getTimeout());
 | 
					        model.setTimeout(client.getTimeout());
 | 
				
			||||||
@@ -79,9 +80,6 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 生成token
 | 
					        // 生成token
 | 
				
			||||||
        LoginHelper.login(loginUser, model);
 | 
					        LoginHelper.login(loginUser, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loginService.recordLogininfor(loginUser.getTenantId(), username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
					 | 
				
			||||||
        loginService.recordLoginInfo(user.getUserId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LoginVo loginVo = new LoginVo();
 | 
					        LoginVo loginVo = new LoginVo();
 | 
				
			||||||
        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
					        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
				
			||||||
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
					        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
				
			||||||
@@ -97,7 +95,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
     * @param uuid     唯一标识
 | 
					     * @param uuid     唯一标识
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private void validateCaptcha(String tenantId, String username, String code, String uuid) {
 | 
					    private void validateCaptcha(String tenantId, String username, String code, String uuid) {
 | 
				
			||||||
        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.defaultString(uuid, "");
 | 
					        String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
 | 
				
			||||||
        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
					        String captcha = RedisUtils.getCacheObject(verifyKey);
 | 
				
			||||||
        RedisUtils.deleteObject(verifyKey);
 | 
					        RedisUtils.deleteObject(verifyKey);
 | 
				
			||||||
        if (captcha == null) {
 | 
					        if (captcha == null) {
 | 
				
			||||||
@@ -110,22 +108,16 @@ public class PasswordAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SysUserVo loadUserByUsername(String tenantId, String username) {
 | 
					    private SysUserVo loadUserByUsername(String username) {
 | 
				
			||||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
					        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
 | 
				
			||||||
            .select(SysUser::getUserName, SysUser::getStatus)
 | 
					 | 
				
			||||||
            .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
					 | 
				
			||||||
            .eq(SysUser::getUserName, username));
 | 
					 | 
				
			||||||
        if (ObjectUtil.isNull(user)) {
 | 
					        if (ObjectUtil.isNull(user)) {
 | 
				
			||||||
            log.info("登录用户:{} 不存在.", username);
 | 
					            log.info("登录用户:{} 不存在.", username);
 | 
				
			||||||
            throw new UserException("user.not.exists", username);
 | 
					            throw new UserException("user.not.exists", username);
 | 
				
			||||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
				
			||||||
            log.info("登录用户:{} 已被停用.", username);
 | 
					            log.info("登录用户:{} 已被停用.", username);
 | 
				
			||||||
            throw new UserException("user.blocked", username);
 | 
					            throw new UserException("user.blocked", username);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (TenantHelper.isEnable()) {
 | 
					        return user;
 | 
				
			||||||
            return userMapper.selectTenantUserByUserName(username, tenantId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return userMapper.selectUserByUserName(username);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,17 @@
 | 
				
			|||||||
package org.dromara.web.service.impl;
 | 
					package org.dromara.web.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
					import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.Constants;
 | 
				
			||||||
import org.dromara.common.core.constant.GlobalConstants;
 | 
					import org.dromara.common.core.constant.GlobalConstants;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
					import org.dromara.common.core.domain.model.LoginUser;
 | 
				
			||||||
import org.dromara.common.core.domain.model.SmsLoginBody;
 | 
					import org.dromara.common.core.domain.model.SmsLoginBody;
 | 
				
			||||||
import org.dromara.common.core.enums.LoginType;
 | 
					import org.dromara.common.core.enums.LoginType;
 | 
				
			||||||
import org.dromara.common.core.enums.UserStatus;
 | 
					 | 
				
			||||||
import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
					import org.dromara.common.core.exception.user.CaptchaExpireException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.UserException;
 | 
					import org.dromara.common.core.exception.user.UserException;
 | 
				
			||||||
import org.dromara.common.core.utils.MessageUtils;
 | 
					import org.dromara.common.core.utils.MessageUtils;
 | 
				
			||||||
@@ -21,8 +21,8 @@ import org.dromara.common.json.utils.JsonUtils;
 | 
				
			|||||||
import org.dromara.common.redis.utils.RedisUtils;
 | 
					import org.dromara.common.redis.utils.RedisUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.SysUser;
 | 
					import org.dromara.system.domain.SysUser;
 | 
				
			||||||
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					import org.dromara.system.domain.vo.SysUserVo;
 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
import org.dromara.web.domain.vo.LoginVo;
 | 
					import org.dromara.web.domain.vo.LoginVo;
 | 
				
			||||||
@@ -44,23 +44,22 @@ public class SmsAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
    private final SysUserMapper userMapper;
 | 
					    private final SysUserMapper userMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public LoginVo login(String body, SysClient client) {
 | 
					    public LoginVo login(String body, SysClientVo client) {
 | 
				
			||||||
        SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
 | 
					        SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        String tenantId = loginBody.getTenantId();
 | 
					        String tenantId = loginBody.getTenantId();
 | 
				
			||||||
        String phonenumber = loginBody.getPhonenumber();
 | 
					        String phonenumber = loginBody.getPhonenumber();
 | 
				
			||||||
        String smsCode = loginBody.getSmsCode();
 | 
					        String smsCode = loginBody.getSmsCode();
 | 
				
			||||||
 | 
					        LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
 | 
				
			||||||
        // 通过手机号查找用户
 | 
					            SysUserVo user = loadUserByPhonenumber(phonenumber);
 | 
				
			||||||
        SysUserVo user = loadUserByPhonenumber(tenantId, phonenumber);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
 | 
					            loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
 | 
				
			||||||
            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
					            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
				
			||||||
        LoginUser loginUser = loginService.buildLoginUser(user);
 | 
					            return loginService.buildLoginUser(user);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        loginUser.setClientKey(client.getClientKey());
 | 
					        loginUser.setClientKey(client.getClientKey());
 | 
				
			||||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
					        loginUser.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        SaLoginModel model = new SaLoginModel();
 | 
					        SaLoginParameter model = new SaLoginParameter();
 | 
				
			||||||
        model.setDevice(client.getDeviceType());
 | 
					        model.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
					        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
				
			||||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
					        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
				
			||||||
        model.setTimeout(client.getTimeout());
 | 
					        model.setTimeout(client.getTimeout());
 | 
				
			||||||
@@ -69,9 +68,6 @@ public class SmsAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 生成token
 | 
					        // 生成token
 | 
				
			||||||
        LoginHelper.login(loginUser, model);
 | 
					        LoginHelper.login(loginUser, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
					 | 
				
			||||||
        loginService.recordLoginInfo(user.getUserId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LoginVo loginVo = new LoginVo();
 | 
					        LoginVo loginVo = new LoginVo();
 | 
				
			||||||
        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
					        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
				
			||||||
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
					        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
				
			||||||
@@ -91,22 +87,16 @@ public class SmsAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        return code.equals(smsCode);
 | 
					        return code.equals(smsCode);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SysUserVo loadUserByPhonenumber(String tenantId, String phonenumber) {
 | 
					    private SysUserVo loadUserByPhonenumber(String phonenumber) {
 | 
				
			||||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
					        SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
 | 
				
			||||||
            .select(SysUser::getPhonenumber, SysUser::getStatus)
 | 
					 | 
				
			||||||
            .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
					 | 
				
			||||||
            .eq(SysUser::getPhonenumber, phonenumber));
 | 
					 | 
				
			||||||
        if (ObjectUtil.isNull(user)) {
 | 
					        if (ObjectUtil.isNull(user)) {
 | 
				
			||||||
            log.info("登录用户:{} 不存在.", phonenumber);
 | 
					            log.info("登录用户:{} 不存在.", phonenumber);
 | 
				
			||||||
            throw new UserException("user.not.exists", phonenumber);
 | 
					            throw new UserException("user.not.exists", phonenumber);
 | 
				
			||||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
				
			||||||
            log.info("登录用户:{} 已被停用.", phonenumber);
 | 
					            log.info("登录用户:{} 已被停用.", phonenumber);
 | 
				
			||||||
            throw new UserException("user.blocked", phonenumber);
 | 
					            throw new UserException("user.blocked", phonenumber);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (TenantHelper.isEnable()) {
 | 
					        return user;
 | 
				
			||||||
            return userMapper.selectTenantUserByPhonenumber(phonenumber, tenantId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return userMapper.selectUserByPhonenumber(phonenumber);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,32 +1,29 @@
 | 
				
			|||||||
package org.dromara.web.service.impl;
 | 
					package org.dromara.web.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
 | 
					import cn.hutool.core.collection.CollUtil;
 | 
				
			||||||
import cn.hutool.core.map.MapUtil;
 | 
					import cn.hutool.core.map.MapUtil;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
import cn.hutool.core.util.StrUtil;
 | 
					 | 
				
			||||||
import cn.hutool.http.HttpUtil;
 | 
					import cn.hutool.http.HttpUtil;
 | 
				
			||||||
import cn.hutool.http.Method;
 | 
					import cn.hutool.http.Method;
 | 
				
			||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 | 
					 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import me.zhyd.oauth.model.AuthResponse;
 | 
					import me.zhyd.oauth.model.AuthResponse;
 | 
				
			||||||
import me.zhyd.oauth.model.AuthUser;
 | 
					import me.zhyd.oauth.model.AuthUser;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.LoginUser;
 | 
					import org.dromara.common.core.domain.model.LoginUser;
 | 
				
			||||||
import org.dromara.common.core.domain.model.SocialLoginBody;
 | 
					import org.dromara.common.core.domain.model.SocialLoginBody;
 | 
				
			||||||
import org.dromara.common.core.enums.UserStatus;
 | 
					 | 
				
			||||||
import org.dromara.common.core.exception.ServiceException;
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
import org.dromara.common.core.exception.user.UserException;
 | 
					import org.dromara.common.core.exception.user.UserException;
 | 
				
			||||||
import org.dromara.common.core.utils.MessageUtils;
 | 
					import org.dromara.common.core.utils.StreamUtils;
 | 
				
			||||||
import org.dromara.common.core.utils.ValidatorUtils;
 | 
					import org.dromara.common.core.utils.ValidatorUtils;
 | 
				
			||||||
import org.dromara.common.json.utils.JsonUtils;
 | 
					import org.dromara.common.json.utils.JsonUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.common.social.config.properties.SocialProperties;
 | 
					import org.dromara.common.social.config.properties.SocialProperties;
 | 
				
			||||||
import org.dromara.common.social.utils.SocialUtils;
 | 
					import org.dromara.common.social.utils.SocialUtils;
 | 
				
			||||||
import org.dromara.common.tenant.helper.TenantHelper;
 | 
					import org.dromara.common.tenant.helper.TenantHelper;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.SysUser;
 | 
					 | 
				
			||||||
import org.dromara.system.domain.vo.SysSocialVo;
 | 
					import org.dromara.system.domain.vo.SysSocialVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					import org.dromara.system.domain.vo.SysUserVo;
 | 
				
			||||||
import org.dromara.system.mapper.SysUserMapper;
 | 
					import org.dromara.system.mapper.SysUserMapper;
 | 
				
			||||||
@@ -36,6 +33,9 @@ import org.dromara.web.service.IAuthStrategy;
 | 
				
			|||||||
import org.dromara.web.service.SysLoginService;
 | 
					import org.dromara.web.service.SysLoginService;
 | 
				
			||||||
import org.springframework.stereotype.Service;
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Optional;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 第三方授权策略
 | 
					 * 第三方授权策略
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -58,7 +58,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
     * @param client   客户端信息
 | 
					     * @param client   客户端信息
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public LoginVo login(String body, SysClient client) {
 | 
					    public LoginVo login(String body, SysClientVo client) {
 | 
				
			||||||
        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
 | 
					        SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
 | 
					        AuthResponse<AuthUser> response = SocialUtils.loginAuth(
 | 
				
			||||||
@@ -78,26 +78,29 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
                    .executeAsync();
 | 
					                    .executeAsync();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SysSocialVo social = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
 | 
					        List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
 | 
				
			||||||
        if (!ObjectUtil.isNotNull(social)) {
 | 
					        if (CollUtil.isEmpty(list)) {
 | 
				
			||||||
            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
 | 
					            throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        // 验证授权表里面的租户id是否包含当前租户id
 | 
					        SysSocialVo social;
 | 
				
			||||||
        String tenantId = social.getTenantId();
 | 
					        if (TenantHelper.isEnable()) {
 | 
				
			||||||
        if (ObjectUtil.isNotNull(social) && StrUtil.isNotBlank(tenantId)
 | 
					            Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
 | 
				
			||||||
                && !tenantId.contains(loginBody.getTenantId())) {
 | 
					            if (opt.isEmpty()) {
 | 
				
			||||||
                throw new ServiceException("对不起,你没有权限登录当前租户!");
 | 
					                throw new ServiceException("对不起,你没有权限登录当前租户!");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            social = opt.get();
 | 
				
			||||||
        // 查找用户
 | 
					        } else {
 | 
				
			||||||
        SysUserVo user = loadUser(tenantId, social.getUserId());
 | 
					            social = list.get(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> {
 | 
				
			||||||
 | 
					            SysUserVo user = loadUser(social.getUserId());
 | 
				
			||||||
            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
					            // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
				
			||||||
        LoginUser loginUser = loginService.buildLoginUser(user);
 | 
					            return loginService.buildLoginUser(user);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        loginUser.setClientKey(client.getClientKey());
 | 
					        loginUser.setClientKey(client.getClientKey());
 | 
				
			||||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
					        loginUser.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        SaLoginModel model = new SaLoginModel();
 | 
					        SaLoginParameter model = new SaLoginParameter();
 | 
				
			||||||
        model.setDevice(client.getDeviceType());
 | 
					        model.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
					        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
				
			||||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
					        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
				
			||||||
        model.setTimeout(client.getTimeout());
 | 
					        model.setTimeout(client.getTimeout());
 | 
				
			||||||
@@ -106,9 +109,6 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 生成token
 | 
					        // 生成token
 | 
				
			||||||
        LoginHelper.login(loginUser, model);
 | 
					        LoginHelper.login(loginUser, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
					 | 
				
			||||||
        loginService.recordLoginInfo(user.getUserId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LoginVo loginVo = new LoginVo();
 | 
					        LoginVo loginVo = new LoginVo();
 | 
				
			||||||
        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
					        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
				
			||||||
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
					        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
				
			||||||
@@ -116,22 +116,16 @@ public class SocialAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        return loginVo;
 | 
					        return loginVo;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private SysUserVo loadUser(String tenantId, Long userId) {
 | 
					    private SysUserVo loadUser(Long userId) {
 | 
				
			||||||
        SysUser user = userMapper.selectOne(new LambdaQueryWrapper<SysUser>()
 | 
					        SysUserVo user = userMapper.selectVoById(userId);
 | 
				
			||||||
                .select(SysUser::getUserName, SysUser::getStatus)
 | 
					 | 
				
			||||||
                .eq(TenantHelper.isEnable(), SysUser::getTenantId, tenantId)
 | 
					 | 
				
			||||||
                .eq(SysUser::getUserId, userId));
 | 
					 | 
				
			||||||
        if (ObjectUtil.isNull(user)) {
 | 
					        if (ObjectUtil.isNull(user)) {
 | 
				
			||||||
            log.info("登录用户:{} 不存在.", "");
 | 
					            log.info("登录用户:{} 不存在.", "");
 | 
				
			||||||
            throw new UserException("user.not.exists", "");
 | 
					            throw new UserException("user.not.exists", "");
 | 
				
			||||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
				
			||||||
            log.info("登录用户:{} 已被停用.", "");
 | 
					            log.info("登录用户:{} 已被停用.", "");
 | 
				
			||||||
            throw new UserException("user.blocked", "");
 | 
					            throw new UserException("user.blocked", "");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (TenantHelper.isEnable()) {
 | 
					        return user;
 | 
				
			||||||
            return userMapper.selectTenantUserByUserName(user.getUserName(), tenantId);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return userMapper.selectUserByUserName(user.getUserName());
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,25 @@
 | 
				
			|||||||
package org.dromara.web.service.impl;
 | 
					package org.dromara.web.service.impl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.stp.SaLoginModel;
 | 
					 | 
				
			||||||
import cn.dev33.satoken.stp.StpUtil;
 | 
					import cn.dev33.satoken.stp.StpUtil;
 | 
				
			||||||
 | 
					import cn.dev33.satoken.stp.parameter.SaLoginParameter;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.common.core.constant.Constants;
 | 
					import me.zhyd.oauth.config.AuthConfig;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.model.AuthCallback;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.model.AuthResponse;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.model.AuthToken;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.model.AuthUser;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.request.AuthRequest;
 | 
				
			||||||
 | 
					import me.zhyd.oauth.request.AuthWechatMiniProgramRequest;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.SystemConstants;
 | 
				
			||||||
import org.dromara.common.core.domain.model.XcxLoginBody;
 | 
					import org.dromara.common.core.domain.model.XcxLoginBody;
 | 
				
			||||||
import org.dromara.common.core.domain.model.XcxLoginUser;
 | 
					import org.dromara.common.core.domain.model.XcxLoginUser;
 | 
				
			||||||
import org.dromara.common.core.enums.UserStatus;
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
import org.dromara.common.core.utils.MessageUtils;
 | 
					 | 
				
			||||||
import org.dromara.common.core.utils.ValidatorUtils;
 | 
					import org.dromara.common.core.utils.ValidatorUtils;
 | 
				
			||||||
import org.dromara.common.json.utils.JsonUtils;
 | 
					import org.dromara.common.json.utils.JsonUtils;
 | 
				
			||||||
import org.dromara.common.satoken.utils.LoginHelper;
 | 
					import org.dromara.common.satoken.utils.LoginHelper;
 | 
				
			||||||
import org.dromara.system.domain.SysClient;
 | 
					import org.dromara.system.domain.vo.SysClientVo;
 | 
				
			||||||
import org.dromara.system.domain.vo.SysUserVo;
 | 
					import org.dromara.system.domain.vo.SysUserVo;
 | 
				
			||||||
import org.dromara.web.domain.vo.LoginVo;
 | 
					import org.dromara.web.domain.vo.LoginVo;
 | 
				
			||||||
import org.dromara.web.service.IAuthStrategy;
 | 
					import org.dromara.web.service.IAuthStrategy;
 | 
				
			||||||
@@ -21,7 +27,7 @@ import org.dromara.web.service.SysLoginService;
 | 
				
			|||||||
import org.springframework.stereotype.Service;
 | 
					import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 邮件认证策略
 | 
					 * 小程序认证策略
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Michelle.Chung
 | 
					 * @author Michelle.Chung
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -33,7 +39,7 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
    private final SysLoginService loginService;
 | 
					    private final SysLoginService loginService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public LoginVo login(String body, SysClient client) {
 | 
					    public LoginVo login(String body, SysClientVo client) {
 | 
				
			||||||
        XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
 | 
					        XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
 | 
				
			||||||
        ValidatorUtils.validate(loginBody);
 | 
					        ValidatorUtils.validate(loginBody);
 | 
				
			||||||
        // xcxCode 为 小程序调用 wx.login 授权后获取
 | 
					        // xcxCode 为 小程序调用 wx.login 授权后获取
 | 
				
			||||||
@@ -41,12 +47,24 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 多个小程序识别使用
 | 
					        // 多个小程序识别使用
 | 
				
			||||||
        String appid = loginBody.getAppid();
 | 
					        String appid = loginBody.getAppid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // todo 以下自行实现
 | 
					 | 
				
			||||||
        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
 | 
					        // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
 | 
				
			||||||
        String openid = "";
 | 
					        AuthRequest authRequest = new AuthWechatMiniProgramRequest(AuthConfig.builder()
 | 
				
			||||||
 | 
					            .clientId(appid).clientSecret("自行填写密钥 可根据不同appid填入不同密钥")
 | 
				
			||||||
 | 
					            .ignoreCheckRedirectUri(true).ignoreCheckState(true).build());
 | 
				
			||||||
 | 
					        AuthCallback authCallback = new AuthCallback();
 | 
				
			||||||
 | 
					        authCallback.setCode(xcxCode);
 | 
				
			||||||
 | 
					        AuthResponse<AuthUser> resp = authRequest.login(authCallback);
 | 
				
			||||||
 | 
					        String openid, unionId;
 | 
				
			||||||
 | 
					        if (resp.ok()) {
 | 
				
			||||||
 | 
					            AuthToken token = resp.getData().getToken();
 | 
				
			||||||
 | 
					            openid = token.getOpenId();
 | 
				
			||||||
 | 
					            // 微信小程序只有关联到微信开放平台下之后才能获取到 unionId,因此unionId不一定能返回。
 | 
				
			||||||
 | 
					            unionId = token.getUnionId();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            throw new ServiceException(resp.getMsg());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
 | 
					        // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
 | 
				
			||||||
        SysUserVo user = loadUserByOpenid(openid);
 | 
					        SysUserVo user = loadUserByOpenid(openid);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
					        // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
 | 
				
			||||||
        XcxLoginUser loginUser = new XcxLoginUser();
 | 
					        XcxLoginUser loginUser = new XcxLoginUser();
 | 
				
			||||||
        loginUser.setTenantId(user.getTenantId());
 | 
					        loginUser.setTenantId(user.getTenantId());
 | 
				
			||||||
@@ -58,8 +76,8 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        loginUser.setDeviceType(client.getDeviceType());
 | 
					        loginUser.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        loginUser.setOpenid(openid);
 | 
					        loginUser.setOpenid(openid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        SaLoginModel model = new SaLoginModel();
 | 
					        SaLoginParameter model = new SaLoginParameter();
 | 
				
			||||||
        model.setDevice(client.getDeviceType());
 | 
					        model.setDeviceType(client.getDeviceType());
 | 
				
			||||||
        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
					        // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
 | 
				
			||||||
        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
					        // 例如: 后台用户30分钟过期 app用户1天过期
 | 
				
			||||||
        model.setTimeout(client.getTimeout());
 | 
					        model.setTimeout(client.getTimeout());
 | 
				
			||||||
@@ -68,9 +86,6 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        // 生成token
 | 
					        // 生成token
 | 
				
			||||||
        LoginHelper.login(loginUser, model);
 | 
					        LoginHelper.login(loginUser, model);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loginService.recordLogininfor(loginUser.getTenantId(), user.getUserName(), Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"));
 | 
					 | 
				
			||||||
        loginService.recordLoginInfo(user.getUserId());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        LoginVo loginVo = new LoginVo();
 | 
					        LoginVo loginVo = new LoginVo();
 | 
				
			||||||
        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
					        loginVo.setAccessToken(StpUtil.getTokenValue());
 | 
				
			||||||
        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
					        loginVo.setExpireIn(StpUtil.getTokenTimeout());
 | 
				
			||||||
@@ -86,7 +101,7 @@ public class XcxAuthStrategy implements IAuthStrategy {
 | 
				
			|||||||
        if (ObjectUtil.isNull(user)) {
 | 
					        if (ObjectUtil.isNull(user)) {
 | 
				
			||||||
            log.info("登录用户:{} 不存在.", openid);
 | 
					            log.info("登录用户:{} 不存在.", openid);
 | 
				
			||||||
            // todo 用户不存在 业务逻辑自行实现
 | 
					            // todo 用户不存在 业务逻辑自行实现
 | 
				
			||||||
        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
 | 
					        } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
 | 
				
			||||||
            log.info("登录用户:{} 已被停用.", openid);
 | 
					            log.info("登录用户:{} 已被停用.", openid);
 | 
				
			||||||
            // todo 用户已被停用 业务逻辑自行实现
 | 
					            // todo 用户已被停用 业务逻辑自行实现
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,24 +5,30 @@ spring.boot.admin.client:
 | 
				
			|||||||
  url: http://localhost:9090/admin
 | 
					  url: http://localhost:9090/admin
 | 
				
			||||||
  instance:
 | 
					  instance:
 | 
				
			||||||
    service-host-type: IP
 | 
					    service-host-type: IP
 | 
				
			||||||
  username: ruoyi
 | 
					    metadata:
 | 
				
			||||||
  password: 123456
 | 
					      username: ${spring.boot.admin.client.username}
 | 
				
			||||||
 | 
					      userpassword: ${spring.boot.admin.client.password}
 | 
				
			||||||
 | 
					  username: @monitor.username@
 | 
				
			||||||
 | 
					  password: @monitor.password@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # powerjob 配置
 | 
					--- # snail-job 配置
 | 
				
			||||||
powerjob:
 | 
					snail-job:
 | 
				
			||||||
  worker:
 | 
					  enabled: true
 | 
				
			||||||
    # 如何开启调度中心请查看文档教程
 | 
					  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
 | 
				
			||||||
    enabled: false
 | 
					  group: "ruoyi_group"
 | 
				
			||||||
    # 需要先在 powerjob 登录页执行应用注册后才能使用
 | 
					  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
 | 
				
			||||||
    app-name: ruoyi-worker
 | 
					  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
 | 
				
			||||||
    enable-test-mode: false
 | 
					  server:
 | 
				
			||||||
    max-appended-wf-context-length: 4096
 | 
					    host: 127.0.0.1
 | 
				
			||||||
    max-result-length: 4096
 | 
					    port: 17888
 | 
				
			||||||
    # 28080 端口 随着主应用端口飘逸 避免集群冲突
 | 
					  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
 | 
				
			||||||
 | 
					  namespace: ${spring.profiles.active}
 | 
				
			||||||
 | 
					  # 随主应用端口漂移
 | 
				
			||||||
  port: 2${server.port}
 | 
					  port: 2${server.port}
 | 
				
			||||||
    protocol: http
 | 
					  # 客户端ip指定
 | 
				
			||||||
    server-address: 127.0.0.1:7700
 | 
					  host:
 | 
				
			||||||
    store-strategy: disk
 | 
					  # RPC类型: netty, grpc
 | 
				
			||||||
 | 
					  rpc-type: grpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # 数据源配置
 | 
					--- # 数据源配置
 | 
				
			||||||
spring:
 | 
					spring:
 | 
				
			||||||
@@ -43,25 +49,23 @@ spring:
 | 
				
			|||||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
					          driverClassName: com.mysql.cj.jdbc.Driver
 | 
				
			||||||
          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
 | 
					          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
 | 
				
			||||||
          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
 | 
					          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
 | 
				
			||||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
 | 
					          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
				
			||||||
          username: root
 | 
					          username: root
 | 
				
			||||||
          password: root
 | 
					          password: root
 | 
				
			||||||
        # 从库数据源
 | 
					#        # 从库数据源
 | 
				
			||||||
        slave:
 | 
					#        slave:
 | 
				
			||||||
          lazy: true
 | 
					#          lazy: true
 | 
				
			||||||
          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
					#          driverClassName: com.mysql.cj.jdbc.Driver
 | 
				
			||||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
 | 
					#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
				
			||||||
          username:
 | 
					#          username:
 | 
				
			||||||
          password:
 | 
					#          password:
 | 
				
			||||||
#        oracle:
 | 
					#        oracle:
 | 
				
			||||||
#          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
#          driverClassName: oracle.jdbc.OracleDriver
 | 
					#          driverClassName: oracle.jdbc.OracleDriver
 | 
				
			||||||
#          url: jdbc:oracle:thin:@//localhost:1521/XE
 | 
					#          url: jdbc:oracle:thin:@//localhost:1521/XE
 | 
				
			||||||
#          username: ROOT
 | 
					#          username: ROOT
 | 
				
			||||||
#          password: root
 | 
					#          password: root
 | 
				
			||||||
#          hikari:
 | 
					 | 
				
			||||||
#            connectionTestQuery: SELECT 1 FROM DUAL
 | 
					 | 
				
			||||||
#        postgres:
 | 
					#        postgres:
 | 
				
			||||||
#          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
#          driverClassName: org.postgresql.Driver
 | 
					#          driverClassName: org.postgresql.Driver
 | 
				
			||||||
@@ -87,8 +91,6 @@ spring:
 | 
				
			|||||||
        idleTimeout: 600000
 | 
					        idleTimeout: 600000
 | 
				
			||||||
        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
					        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
				
			||||||
        maxLifetime: 1800000
 | 
					        maxLifetime: 1800000
 | 
				
			||||||
        # 连接测试query(配置检测连接是否有效)
 | 
					 | 
				
			||||||
        connectionTestQuery: SELECT 1
 | 
					 | 
				
			||||||
        # 多久检查一次连接的活性
 | 
					        # 多久检查一次连接的活性
 | 
				
			||||||
        keepaliveTime: 30000
 | 
					        keepaliveTime: 30000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -101,13 +103,14 @@ spring.data:
 | 
				
			|||||||
    port: 6379
 | 
					    port: 6379
 | 
				
			||||||
    # 数据库索引
 | 
					    # 数据库索引
 | 
				
			||||||
    database: 0
 | 
					    database: 0
 | 
				
			||||||
    # 密码(如没有密码请注释掉)
 | 
					    # redis 密码必须配置
 | 
				
			||||||
    # password:
 | 
					    password: ruoyi123
 | 
				
			||||||
    # 连接超时时间
 | 
					    # 连接超时时间
 | 
				
			||||||
    timeout: 10s
 | 
					    timeout: 10s
 | 
				
			||||||
    # 是否开启ssl
 | 
					    # 是否开启ssl
 | 
				
			||||||
    ssl.enabled: false
 | 
					    ssl.enabled: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# redisson 配置
 | 
				
			||||||
redisson:
 | 
					redisson:
 | 
				
			||||||
  # redis key前缀
 | 
					  # redis key前缀
 | 
				
			||||||
  keyPrefix:
 | 
					  keyPrefix:
 | 
				
			||||||
@@ -117,8 +120,8 @@ redisson:
 | 
				
			|||||||
  nettyThreads: 8
 | 
					  nettyThreads: 8
 | 
				
			||||||
  # 单节点配置
 | 
					  # 单节点配置
 | 
				
			||||||
  singleServerConfig:
 | 
					  singleServerConfig:
 | 
				
			||||||
    # 客户端名称
 | 
					    # 客户端名称 不能用中文
 | 
				
			||||||
    clientName: ${ruoyi.name}
 | 
					    clientName: RuoYi-Vue-Plus
 | 
				
			||||||
    # 最小空闲连接数
 | 
					    # 最小空闲连接数
 | 
				
			||||||
    connectionMinimumIdleSize: 8
 | 
					    connectionMinimumIdleSize: 8
 | 
				
			||||||
    # 连接池大小
 | 
					    # 连接池大小
 | 
				
			||||||
@@ -153,36 +156,40 @@ mail:
 | 
				
			|||||||
  connectionTimeout: 0
 | 
					  connectionTimeout: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
 | 
					--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
 | 
				
			||||||
# https://wind.kim/doc/start 文档地址 各个厂商可同时使用
 | 
					# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
 | 
				
			||||||
sms:
 | 
					sms:
 | 
				
			||||||
  # 阿里云 dysmsapi.aliyuncs.com
 | 
					  # 配置源类型用于标定配置来源(interface,yaml)
 | 
				
			||||||
  alibaba:
 | 
					  config-type: yaml
 | 
				
			||||||
    #请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
 | 
					  # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
 | 
				
			||||||
    requestUrl: dysmsapi.aliyuncs.com
 | 
					  restricted: true
 | 
				
			||||||
    #阿里云的accessKey
 | 
					  # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
 | 
				
			||||||
    accessKeyId: xxxxxxx
 | 
					  minute-max: 1
 | 
				
			||||||
    #阿里云的accessKeySecret
 | 
					  # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
 | 
				
			||||||
    accessKeySecret: xxxxxxx
 | 
					  account-max: 30
 | 
				
			||||||
    #短信签名
 | 
					  # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
 | 
				
			||||||
    signature: 测试
 | 
					  blends:
 | 
				
			||||||
  tencent:
 | 
					    # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
 | 
				
			||||||
    #请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置
 | 
					    # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
 | 
				
			||||||
    requestUrl: sms.tencentcloudapi.com
 | 
					    config1:
 | 
				
			||||||
    #腾讯云的accessKey
 | 
					      # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
 | 
				
			||||||
    accessKeyId: xxxxxxx
 | 
					      supplier: alibaba
 | 
				
			||||||
    #腾讯云的accessKeySecret
 | 
					      # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
 | 
				
			||||||
    accessKeySecret: xxxxxxx
 | 
					      access-key-id: 您的accessKey
 | 
				
			||||||
    #短信签名
 | 
					      # 称为accessSecret有些称之为apiSecret
 | 
				
			||||||
    signature: 测试
 | 
					      access-key-secret: 您的accessKeySecret
 | 
				
			||||||
    #短信sdkAppId
 | 
					      signature: 您的短信签名
 | 
				
			||||||
    sdkAppId: appid
 | 
					      sdk-app-id: 您的sdkAppId
 | 
				
			||||||
    #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
 | 
					    config2:
 | 
				
			||||||
    territory: ap-guangzhou
 | 
					      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
 | 
				
			||||||
 | 
					      supplier: tencent
 | 
				
			||||||
 | 
					      access-key-id: 您的accessKey
 | 
				
			||||||
 | 
					      access-key-secret: 您的accessKeySecret
 | 
				
			||||||
 | 
					      signature: 您的短信签名
 | 
				
			||||||
 | 
					      sdk-app-id: 您的sdkAppId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # 三方授权
 | 
					--- # 三方授权
 | 
				
			||||||
justauth:
 | 
					justauth:
 | 
				
			||||||
  enabled: true
 | 
					 | 
				
			||||||
  # 前端外网访问地址
 | 
					  # 前端外网访问地址
 | 
				
			||||||
  address: http://localhost:80
 | 
					  address: http://localhost:80
 | 
				
			||||||
  type:
 | 
					  type:
 | 
				
			||||||
@@ -193,6 +200,13 @@ justauth:
 | 
				
			|||||||
      client-id: 876892492581044224
 | 
					      client-id: 876892492581044224
 | 
				
			||||||
      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
 | 
					      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
 | 
				
			||||||
      redirect-uri: ${justauth.address}/social-callback?source=maxkey
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=maxkey
 | 
				
			||||||
 | 
					    topiam:
 | 
				
			||||||
 | 
					      # topiam 服务器地址
 | 
				
			||||||
 | 
					      server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
 | 
				
			||||||
 | 
					      client-id: 449c4*********937************759
 | 
				
			||||||
 | 
					      client-secret: ac7***********1e0************28d
 | 
				
			||||||
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=topiam
 | 
				
			||||||
 | 
					      scopes: [openid, email, phone, profile]
 | 
				
			||||||
    qq:
 | 
					    qq:
 | 
				
			||||||
      client-id: 10**********6
 | 
					      client-id: 10**********6
 | 
				
			||||||
      client-secret: 1f7d08**********5b7**********29e
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
@@ -249,3 +263,10 @@ justauth:
 | 
				
			|||||||
      client-id: 10**********6
 | 
					      client-id: 10**********6
 | 
				
			||||||
      client-secret: 1f7d08**********5b7**********29e
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
				
			||||||
 | 
					    gitea:
 | 
				
			||||||
 | 
					      # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
 | 
				
			||||||
 | 
					      # gitea 服务器地址
 | 
				
			||||||
 | 
					      server-url: https://demo.gitea.com
 | 
				
			||||||
 | 
					      client-id: 10**********6
 | 
				
			||||||
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=gitea
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,24 +8,30 @@ spring.boot.admin.client:
 | 
				
			|||||||
  url: http://localhost:9090/admin
 | 
					  url: http://localhost:9090/admin
 | 
				
			||||||
  instance:
 | 
					  instance:
 | 
				
			||||||
    service-host-type: IP
 | 
					    service-host-type: IP
 | 
				
			||||||
  username: ruoyi
 | 
					    metadata:
 | 
				
			||||||
  password: 123456
 | 
					      username: ${spring.boot.admin.client.username}
 | 
				
			||||||
 | 
					      userpassword: ${spring.boot.admin.client.password}
 | 
				
			||||||
 | 
					  username: @monitor.username@
 | 
				
			||||||
 | 
					  password: @monitor.password@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # powerjob 配置
 | 
					--- # snail-job 配置
 | 
				
			||||||
powerjob:
 | 
					snail-job:
 | 
				
			||||||
  worker:
 | 
					  enabled: true
 | 
				
			||||||
    # 如何开启调度中心请查看文档教程
 | 
					  # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
 | 
				
			||||||
    enabled: false
 | 
					  group: "ruoyi_group"
 | 
				
			||||||
    # 需要先在 powerjob 登录页执行应用注册后才能使用
 | 
					  # SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
 | 
				
			||||||
    app-name: ruoyi-worker
 | 
					  token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
 | 
				
			||||||
    enable-test-mode: false
 | 
					  server:
 | 
				
			||||||
    max-appended-wf-context-length: 4096
 | 
					    host: 127.0.0.1
 | 
				
			||||||
    max-result-length: 4096
 | 
					    port: 17888
 | 
				
			||||||
    # 28080 端口 随着主应用端口飘逸 避免集群冲突
 | 
					  # 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
 | 
				
			||||||
 | 
					  namespace: ${spring.profiles.active}
 | 
				
			||||||
 | 
					  # 随主应用端口漂移
 | 
				
			||||||
  port: 2${server.port}
 | 
					  port: 2${server.port}
 | 
				
			||||||
    protocol: http
 | 
					  # 客户端ip指定
 | 
				
			||||||
    server-address: 127.0.0.1:7700
 | 
					  host:
 | 
				
			||||||
    store-strategy: disk
 | 
					  # RPC类型: netty, grpc
 | 
				
			||||||
 | 
					  rpc-type: grpc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # 数据源配置
 | 
					--- # 数据源配置
 | 
				
			||||||
spring:
 | 
					spring:
 | 
				
			||||||
@@ -46,25 +52,23 @@ spring:
 | 
				
			|||||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
					          driverClassName: com.mysql.cj.jdbc.Driver
 | 
				
			||||||
          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
 | 
					          # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
 | 
				
			||||||
          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
 | 
					          # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
 | 
				
			||||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
 | 
					          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
				
			||||||
          username: root
 | 
					          username: root
 | 
				
			||||||
          password: root
 | 
					          password: root
 | 
				
			||||||
        # 从库数据源
 | 
					#        # 从库数据源
 | 
				
			||||||
        slave:
 | 
					#        slave:
 | 
				
			||||||
          lazy: true
 | 
					#          lazy: true
 | 
				
			||||||
          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
          driverClassName: com.mysql.cj.jdbc.Driver
 | 
					#          driverClassName: com.mysql.cj.jdbc.Driver
 | 
				
			||||||
          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true
 | 
					#          url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
 | 
				
			||||||
          username:
 | 
					#          username:
 | 
				
			||||||
          password:
 | 
					#          password:
 | 
				
			||||||
#        oracle:
 | 
					#        oracle:
 | 
				
			||||||
#          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
#          driverClassName: oracle.jdbc.OracleDriver
 | 
					#          driverClassName: oracle.jdbc.OracleDriver
 | 
				
			||||||
#          url: jdbc:oracle:thin:@//localhost:1521/XE
 | 
					#          url: jdbc:oracle:thin:@//localhost:1521/XE
 | 
				
			||||||
#          username: ROOT
 | 
					#          username: ROOT
 | 
				
			||||||
#          password: root
 | 
					#          password: root
 | 
				
			||||||
#          hikari:
 | 
					 | 
				
			||||||
#            connectionTestQuery: SELECT 1 FROM DUAL
 | 
					 | 
				
			||||||
#        postgres:
 | 
					#        postgres:
 | 
				
			||||||
#          type: ${spring.datasource.type}
 | 
					#          type: ${spring.datasource.type}
 | 
				
			||||||
#          driverClassName: org.postgresql.Driver
 | 
					#          driverClassName: org.postgresql.Driver
 | 
				
			||||||
@@ -90,8 +94,6 @@ spring:
 | 
				
			|||||||
        idleTimeout: 600000
 | 
					        idleTimeout: 600000
 | 
				
			||||||
        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
					        # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
 | 
				
			||||||
        maxLifetime: 1800000
 | 
					        maxLifetime: 1800000
 | 
				
			||||||
        # 连接测试query(配置检测连接是否有效)
 | 
					 | 
				
			||||||
        connectionTestQuery: SELECT 1
 | 
					 | 
				
			||||||
        # 多久检查一次连接的活性
 | 
					        # 多久检查一次连接的活性
 | 
				
			||||||
        keepaliveTime: 30000
 | 
					        keepaliveTime: 30000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -104,13 +106,14 @@ spring.data:
 | 
				
			|||||||
    port: 6379
 | 
					    port: 6379
 | 
				
			||||||
    # 数据库索引
 | 
					    # 数据库索引
 | 
				
			||||||
    database: 0
 | 
					    database: 0
 | 
				
			||||||
    # 密码(如没有密码请注释掉)
 | 
					    # redis 密码必须配置
 | 
				
			||||||
    # password:
 | 
					    password: ruoyi123
 | 
				
			||||||
    # 连接超时时间
 | 
					    # 连接超时时间
 | 
				
			||||||
    timeout: 10s
 | 
					    timeout: 10s
 | 
				
			||||||
    # 是否开启ssl
 | 
					    # 是否开启ssl
 | 
				
			||||||
    ssl.enabled: false
 | 
					    ssl.enabled: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# redisson 配置
 | 
				
			||||||
redisson:
 | 
					redisson:
 | 
				
			||||||
  # redis key前缀
 | 
					  # redis key前缀
 | 
				
			||||||
  keyPrefix:
 | 
					  keyPrefix:
 | 
				
			||||||
@@ -120,8 +123,8 @@ redisson:
 | 
				
			|||||||
  nettyThreads: 32
 | 
					  nettyThreads: 32
 | 
				
			||||||
  # 单节点配置
 | 
					  # 单节点配置
 | 
				
			||||||
  singleServerConfig:
 | 
					  singleServerConfig:
 | 
				
			||||||
    # 客户端名称
 | 
					    # 客户端名称 不能用中文
 | 
				
			||||||
    clientName: ${ruoyi.name}
 | 
					    clientName: RuoYi-Vue-Plus
 | 
				
			||||||
    # 最小空闲连接数
 | 
					    # 最小空闲连接数
 | 
				
			||||||
    connectionMinimumIdleSize: 32
 | 
					    connectionMinimumIdleSize: 32
 | 
				
			||||||
    # 连接池大小
 | 
					    # 连接池大小
 | 
				
			||||||
@@ -156,35 +159,39 @@ mail:
 | 
				
			|||||||
  connectionTimeout: 0
 | 
					  connectionTimeout: 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
 | 
					--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
 | 
				
			||||||
# https://wind.kim/doc/start 文档地址 各个厂商可同时使用
 | 
					# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
 | 
				
			||||||
sms:
 | 
					sms:
 | 
				
			||||||
  # 阿里云 dysmsapi.aliyuncs.com
 | 
					  # 配置源类型用于标定配置来源(interface,yaml)
 | 
				
			||||||
  alibaba:
 | 
					  config-type: yaml
 | 
				
			||||||
    #请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
 | 
					  # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
 | 
				
			||||||
    requestUrl: dysmsapi.aliyuncs.com
 | 
					  restricted: true
 | 
				
			||||||
    #阿里云的accessKey
 | 
					  # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
 | 
				
			||||||
    accessKeyId: xxxxxxx
 | 
					  minute-max: 1
 | 
				
			||||||
    #阿里云的accessKeySecret
 | 
					  # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
 | 
				
			||||||
    accessKeySecret: xxxxxxx
 | 
					  account-max: 30
 | 
				
			||||||
    #短信签名
 | 
					  # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
 | 
				
			||||||
    signature: 测试
 | 
					  blends:
 | 
				
			||||||
  tencent:
 | 
					    # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
 | 
				
			||||||
    #请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置
 | 
					    # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
 | 
				
			||||||
    requestUrl: sms.tencentcloudapi.com
 | 
					    config1:
 | 
				
			||||||
    #腾讯云的accessKey
 | 
					      # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
 | 
				
			||||||
    accessKeyId: xxxxxxx
 | 
					      supplier: alibaba
 | 
				
			||||||
    #腾讯云的accessKeySecret
 | 
					      # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
 | 
				
			||||||
    accessKeySecret: xxxxxxx
 | 
					      access-key-id: 您的accessKey
 | 
				
			||||||
    #短信签名
 | 
					      # 称为accessSecret有些称之为apiSecret
 | 
				
			||||||
    signature: 测试
 | 
					      access-key-secret: 您的accessKeySecret
 | 
				
			||||||
    #短信sdkAppId
 | 
					      signature: 您的短信签名
 | 
				
			||||||
    sdkAppId: appid
 | 
					      sdk-app-id: 您的sdkAppId
 | 
				
			||||||
    #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
 | 
					    config2:
 | 
				
			||||||
    territory: ap-guangzhou
 | 
					      # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
 | 
				
			||||||
 | 
					      supplier: tencent
 | 
				
			||||||
 | 
					      access-key-id: 您的accessKey
 | 
				
			||||||
 | 
					      access-key-secret: 您的accessKeySecret
 | 
				
			||||||
 | 
					      signature: 您的短信签名
 | 
				
			||||||
 | 
					      sdk-app-id: 您的sdkAppId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # 三方授权
 | 
					--- # 三方授权
 | 
				
			||||||
justauth:
 | 
					justauth:
 | 
				
			||||||
  enabled: true
 | 
					 | 
				
			||||||
  # 前端外网访问地址
 | 
					  # 前端外网访问地址
 | 
				
			||||||
  address: http://localhost:80
 | 
					  address: http://localhost:80
 | 
				
			||||||
  type:
 | 
					  type:
 | 
				
			||||||
@@ -195,6 +202,13 @@ justauth:
 | 
				
			|||||||
      client-id: 876892492581044224
 | 
					      client-id: 876892492581044224
 | 
				
			||||||
      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
 | 
					      client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
 | 
				
			||||||
      redirect-uri: ${justauth.address}/social-callback?source=maxkey
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=maxkey
 | 
				
			||||||
 | 
					    topiam:
 | 
				
			||||||
 | 
					      # topiam 服务器地址
 | 
				
			||||||
 | 
					      server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
 | 
				
			||||||
 | 
					      client-id: 449c4*********937************759
 | 
				
			||||||
 | 
					      client-secret: ac7***********1e0************28d
 | 
				
			||||||
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=topiam
 | 
				
			||||||
 | 
					      scopes: [ openid, email, phone, profile ]
 | 
				
			||||||
    qq:
 | 
					    qq:
 | 
				
			||||||
      client-id: 10**********6
 | 
					      client-id: 10**********6
 | 
				
			||||||
      client-secret: 1f7d08**********5b7**********29e
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
@@ -251,3 +265,10 @@ justauth:
 | 
				
			|||||||
      client-id: 10**********6
 | 
					      client-id: 10**********6
 | 
				
			||||||
      client-secret: 1f7d08**********5b7**********29e
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=gitlab
 | 
				
			||||||
 | 
					    gitea:
 | 
				
			||||||
 | 
					      # 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
 | 
				
			||||||
 | 
					      # gitea 服务器地址
 | 
				
			||||||
 | 
					      server-url: https://demo.gitea.com
 | 
				
			||||||
 | 
					      client-id: 10**********6
 | 
				
			||||||
 | 
					      client-secret: 1f7d08**********5b7**********29e
 | 
				
			||||||
 | 
					      redirect-uri: ${justauth.address}/social-callback?source=gitea
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,24 +1,3 @@
 | 
				
			|||||||
# 项目相关配置
 | 
					 | 
				
			||||||
ruoyi:
 | 
					 | 
				
			||||||
  # 名称
 | 
					 | 
				
			||||||
  name: RuoYi-Vue-Plus
 | 
					 | 
				
			||||||
  # 版本
 | 
					 | 
				
			||||||
  version: ${revision}
 | 
					 | 
				
			||||||
  # 版权年份
 | 
					 | 
				
			||||||
  copyrightYear: 2023
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
captcha:
 | 
					 | 
				
			||||||
  enable: true
 | 
					 | 
				
			||||||
  # 页面 <参数设置> 可开启关闭 验证码校验
 | 
					 | 
				
			||||||
  # 验证码类型 math 数组计算 char 字符验证
 | 
					 | 
				
			||||||
  type: MATH
 | 
					 | 
				
			||||||
  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
 | 
					 | 
				
			||||||
  category: CIRCLE
 | 
					 | 
				
			||||||
  # 数字验证码位数
 | 
					 | 
				
			||||||
  numberLength: 1
 | 
					 | 
				
			||||||
  # 字符验证码长度
 | 
					 | 
				
			||||||
  charLength: 4
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# 开发环境配置
 | 
					# 开发环境配置
 | 
				
			||||||
server:
 | 
					server:
 | 
				
			||||||
  # 服务器的HTTP端口,默认为8080
 | 
					  # 服务器的HTTP端口,默认为8080
 | 
				
			||||||
@@ -41,12 +20,25 @@ server:
 | 
				
			|||||||
      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
 | 
					      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
 | 
				
			||||||
      worker: 256
 | 
					      worker: 256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					captcha:
 | 
				
			||||||
 | 
					  # 是否启用验证码校验
 | 
				
			||||||
 | 
					  enable: true
 | 
				
			||||||
 | 
					  # 验证码类型 math 数组计算 char 字符验证
 | 
				
			||||||
 | 
					  type: MATH
 | 
				
			||||||
 | 
					  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
 | 
				
			||||||
 | 
					  category: CIRCLE
 | 
				
			||||||
 | 
					  # 数字验证码位数
 | 
				
			||||||
 | 
					  numberLength: 1
 | 
				
			||||||
 | 
					  # 字符验证码长度
 | 
				
			||||||
 | 
					  charLength: 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 日志配置
 | 
					# 日志配置
 | 
				
			||||||
logging:
 | 
					logging:
 | 
				
			||||||
  level:
 | 
					  level:
 | 
				
			||||||
    org.dromara: @logging.level@
 | 
					    org.dromara: @logging.level@
 | 
				
			||||||
    org.springframework: warn
 | 
					    org.springframework: warn
 | 
				
			||||||
    tech.powerjob.worker.background: warn
 | 
					    org.mybatis.spring.mapper: error
 | 
				
			||||||
 | 
					    org.apache.fury: warn
 | 
				
			||||||
  config: classpath:logback-plus.xml
 | 
					  config: classpath:logback-plus.xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 用户配置
 | 
					# 用户配置
 | 
				
			||||||
@@ -60,7 +52,11 @@ user:
 | 
				
			|||||||
# Spring配置
 | 
					# Spring配置
 | 
				
			||||||
spring:
 | 
					spring:
 | 
				
			||||||
  application:
 | 
					  application:
 | 
				
			||||||
    name: ${ruoyi.name}
 | 
					    name: RuoYi-Vue-Plus
 | 
				
			||||||
 | 
					  threads:
 | 
				
			||||||
 | 
					    # 开启虚拟线程 仅jdk21可用
 | 
				
			||||||
 | 
					    virtual:
 | 
				
			||||||
 | 
					      enabled: false
 | 
				
			||||||
  # 资源信息
 | 
					  # 资源信息
 | 
				
			||||||
  messages:
 | 
					  messages:
 | 
				
			||||||
    # 国际化资源文件路径
 | 
					    # 国际化资源文件路径
 | 
				
			||||||
@@ -75,6 +71,8 @@ spring:
 | 
				
			|||||||
      # 设置总上传的文件大小
 | 
					      # 设置总上传的文件大小
 | 
				
			||||||
      max-request-size: 20MB
 | 
					      max-request-size: 20MB
 | 
				
			||||||
  mvc:
 | 
					  mvc:
 | 
				
			||||||
 | 
					    # 设置静态资源路径 防止所有请求都去查静态资源
 | 
				
			||||||
 | 
					    static-path-pattern: /static/**
 | 
				
			||||||
    format:
 | 
					    format:
 | 
				
			||||||
      date-time: yyyy-MM-dd HH:mm:ss
 | 
					      date-time: yyyy-MM-dd HH:mm:ss
 | 
				
			||||||
  jackson:
 | 
					  jackson:
 | 
				
			||||||
@@ -104,20 +102,15 @@ sa-token:
 | 
				
			|||||||
security:
 | 
					security:
 | 
				
			||||||
  # 排除路径
 | 
					  # 排除路径
 | 
				
			||||||
  excludes:
 | 
					  excludes:
 | 
				
			||||||
    # 静态资源
 | 
					 | 
				
			||||||
    - /*.html
 | 
					    - /*.html
 | 
				
			||||||
    - /**/*.html
 | 
					    - /**/*.html
 | 
				
			||||||
    - /**/*.css
 | 
					    - /**/*.css
 | 
				
			||||||
    - /**/*.js
 | 
					    - /**/*.js
 | 
				
			||||||
    # 公共路径
 | 
					 | 
				
			||||||
    - /favicon.ico
 | 
					    - /favicon.ico
 | 
				
			||||||
    - /error
 | 
					    - /error
 | 
				
			||||||
    # swagger 文档配置
 | 
					 | 
				
			||||||
    - /*/api-docs
 | 
					    - /*/api-docs
 | 
				
			||||||
    - /*/api-docs/**
 | 
					    - /*/api-docs/**
 | 
				
			||||||
    # actuator 监控配置
 | 
					    - /warm-flow-ui/token-name
 | 
				
			||||||
    - /actuator
 | 
					 | 
				
			||||||
    - /actuator/**
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 多租户配置
 | 
					# 多租户配置
 | 
				
			||||||
tenant:
 | 
					tenant:
 | 
				
			||||||
@@ -133,12 +126,14 @@ tenant:
 | 
				
			|||||||
    - sys_user_post
 | 
					    - sys_user_post
 | 
				
			||||||
    - sys_user_role
 | 
					    - sys_user_role
 | 
				
			||||||
    - sys_client
 | 
					    - sys_client
 | 
				
			||||||
 | 
					    - sys_oss_config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# MyBatisPlus配置
 | 
					# MyBatisPlus配置
 | 
				
			||||||
# https://baomidou.com/config/
 | 
					# https://baomidou.com/config/
 | 
				
			||||||
mybatis-plus:
 | 
					mybatis-plus:
 | 
				
			||||||
  # 不支持多包, 如有需要可在注解配置 或 提升扫包等级
 | 
					  # 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
 | 
				
			||||||
  # 例如 com.**.**.mapper
 | 
					  enableLogicDelete: true
 | 
				
			||||||
 | 
					  # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
 | 
				
			||||||
  mapperPackage: org.dromara.**.mapper
 | 
					  mapperPackage: org.dromara.**.mapper
 | 
				
			||||||
  # 对应的 XML 文件位置
 | 
					  # 对应的 XML 文件位置
 | 
				
			||||||
  mapperLocations: classpath*:mapper/**/*Mapper.xml
 | 
					  mapperLocations: classpath*:mapper/**/*Mapper.xml
 | 
				
			||||||
@@ -171,20 +166,20 @@ api-decrypt:
 | 
				
			|||||||
  enabled: true
 | 
					  enabled: true
 | 
				
			||||||
  # AES 加密头标识
 | 
					  # AES 加密头标识
 | 
				
			||||||
  headerFlag: encrypt-key
 | 
					  headerFlag: encrypt-key
 | 
				
			||||||
  # 公私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
 | 
					  # 响应加密公钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
 | 
				
			||||||
  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
 | 
					  # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
 | 
				
			||||||
 | 
					  publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
 | 
				
			||||||
 | 
					  # 请求解密私钥 非对称算法的公私钥 如:SM2,RSA 使用者请自行更换
 | 
				
			||||||
 | 
					  # 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
 | 
				
			||||||
  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
 | 
					  privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
springdoc:
 | 
					springdoc:
 | 
				
			||||||
  api-docs:
 | 
					  api-docs:
 | 
				
			||||||
    # 是否开启接口文档
 | 
					    # 是否开启接口文档
 | 
				
			||||||
    enabled: true
 | 
					    enabled: true
 | 
				
			||||||
#  swagger-ui:
 | 
					 | 
				
			||||||
#    # 持久化认证数据
 | 
					 | 
				
			||||||
#    persistAuthorization: true
 | 
					 | 
				
			||||||
  info:
 | 
					  info:
 | 
				
			||||||
    # 标题
 | 
					    # 标题
 | 
				
			||||||
    title: '标题:${ruoyi.name}多租户管理系统_接口文档'
 | 
					    title: '标题:RuoYi-Vue-Plus多租户管理系统_接口文档'
 | 
				
			||||||
    # 描述
 | 
					    # 描述
 | 
				
			||||||
    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
 | 
					    description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
 | 
				
			||||||
    # 版本
 | 
					    # 版本
 | 
				
			||||||
@@ -211,17 +206,19 @@ springdoc:
 | 
				
			|||||||
      packages-to-scan: org.dromara.system
 | 
					      packages-to-scan: org.dromara.system
 | 
				
			||||||
    - group: 4.代码生成模块
 | 
					    - group: 4.代码生成模块
 | 
				
			||||||
      packages-to-scan: org.dromara.generator
 | 
					      packages-to-scan: org.dromara.generator
 | 
				
			||||||
 | 
					    - group: 5.工作流模块
 | 
				
			||||||
 | 
					      packages-to-scan: org.dromara.workflow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 防止XSS攻击
 | 
					# 防止XSS攻击
 | 
				
			||||||
xss:
 | 
					xss:
 | 
				
			||||||
  # 过滤开关
 | 
					  # 过滤开关
 | 
				
			||||||
  enabled: true
 | 
					  enabled: true
 | 
				
			||||||
  # 排除链接(多个用逗号分隔)
 | 
					  # 排除链接(多个用逗号分隔)
 | 
				
			||||||
  excludes: /system/notice
 | 
					  excludeUrls:
 | 
				
			||||||
  # 匹配链接
 | 
					    - /system/notice
 | 
				
			||||||
  urlPatterns: /system/*,/monitor/*,/tool/*
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 全局线程池相关配置
 | 
					# 全局线程池相关配置
 | 
				
			||||||
 | 
					# 如使用JDK21请直接使用虚拟线程 不要开启此配置
 | 
				
			||||||
thread-pool:
 | 
					thread-pool:
 | 
				
			||||||
  # 是否开启线程池
 | 
					  # 是否开启线程池
 | 
				
			||||||
  enabled: false
 | 
					  enabled: false
 | 
				
			||||||
@@ -249,11 +246,33 @@ management:
 | 
				
			|||||||
    logfile:
 | 
					    logfile:
 | 
				
			||||||
      external-file: ./logs/sys-console.log
 | 
					      external-file: ./logs/sys-console.log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- # 默认/推荐使用sse推送
 | 
				
			||||||
 | 
					sse:
 | 
				
			||||||
 | 
					  enabled: true
 | 
				
			||||||
 | 
					  path: /resource/sse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
--- # websocket
 | 
					--- # websocket
 | 
				
			||||||
websocket:
 | 
					websocket:
 | 
				
			||||||
  # 如果关闭 需要和前端开关一起关闭
 | 
					  # 如果关闭 需要和前端开关一起关闭
 | 
				
			||||||
  enabled: true
 | 
					  enabled: false
 | 
				
			||||||
  # 路径
 | 
					  # 路径
 | 
				
			||||||
  path: /resource/websocket
 | 
					  path: /resource/websocket
 | 
				
			||||||
  # 设置访问源地址
 | 
					  # 设置访问源地址
 | 
				
			||||||
  allowedOrigins: '*'
 | 
					  allowedOrigins: '*'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--- # warm-flow工作流配置
 | 
				
			||||||
 | 
					warm-flow:
 | 
				
			||||||
 | 
					  # 是否开启工作流,默认true
 | 
				
			||||||
 | 
					  enabled: true
 | 
				
			||||||
 | 
					  # 是否开启设计器ui
 | 
				
			||||||
 | 
					  ui: true
 | 
				
			||||||
 | 
					  # 默认Authorization,如果有多个token,用逗号分隔
 | 
				
			||||||
 | 
					  token-name: ${sa-token.token-name},clientid
 | 
				
			||||||
 | 
					  # 流程状态对应的三元色
 | 
				
			||||||
 | 
					  chart-status-color:
 | 
				
			||||||
 | 
					    ## 未办理
 | 
				
			||||||
 | 
					    - 62,62,62
 | 
				
			||||||
 | 
					    ## 待办理
 | 
				
			||||||
 | 
					    - 255,205,23
 | 
				
			||||||
 | 
					    ## 已办理
 | 
				
			||||||
 | 
					    - 157,255,0
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
 | 
				
			|||||||
email.code.not.blank=邮箱验证码不能为空
 | 
					email.code.not.blank=邮箱验证码不能为空
 | 
				
			||||||
email.code.retry.limit.count=邮箱验证码输入错误{0}次
 | 
					email.code.retry.limit.count=邮箱验证码输入错误{0}次
 | 
				
			||||||
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
 | 
					email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
 | 
				
			||||||
xcx.code.not.blank=小程序code不能为空
 | 
					xcx.code.not.blank=小程序[code]不能为空
 | 
				
			||||||
 | 
					social.source.not.blank=第三方登录平台[source]不能为空
 | 
				
			||||||
 | 
					social.code.not.blank=第三方登录平台[code]不能为空
 | 
				
			||||||
 | 
					social.state.not.blank=第三方登录平台[state]不能为空
 | 
				
			||||||
##租户
 | 
					##租户
 | 
				
			||||||
tenant.number.not.blank=租户编号不能为空
 | 
					tenant.number.not.blank=租户编号不能为空
 | 
				
			||||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
					tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {
 | 
				
			|||||||
email.code.not.blank=Email code cannot be blank
 | 
					email.code.not.blank=Email code cannot be blank
 | 
				
			||||||
email.code.retry.limit.count=Email code input error {0} times
 | 
					email.code.retry.limit.count=Email code input error {0} times
 | 
				
			||||||
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
 | 
					email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
 | 
				
			||||||
xcx.code.not.blank=Mini program code cannot be blank
 | 
					xcx.code.not.blank=Mini program [code] cannot be blank
 | 
				
			||||||
 | 
					social.source.not.blank=Social login platform [source] cannot be blank
 | 
				
			||||||
 | 
					social.code.not.blank=Social login platform [code] cannot be blank
 | 
				
			||||||
 | 
					social.state.not.blank=Social login platform [state] cannot be blank
 | 
				
			||||||
##租户
 | 
					##租户
 | 
				
			||||||
tenant.number.not.blank=Tenant number cannot be blank
 | 
					tenant.number.not.blank=Tenant number cannot be blank
 | 
				
			||||||
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
 | 
					tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -50,7 +50,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
 | 
				
			|||||||
email.code.not.blank=邮箱验证码不能为空
 | 
					email.code.not.blank=邮箱验证码不能为空
 | 
				
			||||||
email.code.retry.limit.count=邮箱验证码输入错误{0}次
 | 
					email.code.retry.limit.count=邮箱验证码输入错误{0}次
 | 
				
			||||||
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
 | 
					email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
 | 
				
			||||||
xcx.code.not.blank=小程序code不能为空
 | 
					xcx.code.not.blank=小程序[code]不能为空
 | 
				
			||||||
 | 
					social.source.not.blank=第三方登录平台[source]不能为空
 | 
				
			||||||
 | 
					social.code.not.blank=第三方登录平台[code]不能为空
 | 
				
			||||||
 | 
					social.state.not.blank=第三方登录平台[state]不能为空
 | 
				
			||||||
##租户
 | 
					##租户
 | 
				
			||||||
tenant.number.not.blank=租户编号不能为空
 | 
					tenant.number.not.blank=租户编号不能为空
 | 
				
			||||||
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
					tenant.not.exists=对不起, 您的租户不存在,请联系管理员
 | 
				
			||||||
 
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							@@ -2,7 +2,7 @@
 | 
				
			|||||||
<configuration>
 | 
					<configuration>
 | 
				
			||||||
    <property name="log.path" value="./logs"/>
 | 
					    <property name="log.path" value="./logs"/>
 | 
				
			||||||
    <property name="console.log.pattern"
 | 
					    <property name="console.log.pattern"
 | 
				
			||||||
              value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
 | 
					              value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
 | 
				
			||||||
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
 | 
					    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- 控制台输出 -->
 | 
					    <!-- 控制台输出 -->
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
package org.dromara.test;
 | 
					package org.dromara.test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.dromara.common.core.config.RuoYiConfig;
 | 
					import org.dromara.common.web.config.properties.CaptchaProperties;
 | 
				
			||||||
import org.junit.jupiter.api.*;
 | 
					import org.junit.jupiter.api.*;
 | 
				
			||||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
					import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
					import org.springframework.boot.test.context.SpringBootTest;
 | 
				
			||||||
@@ -17,19 +17,19 @@ import java.util.concurrent.TimeUnit;
 | 
				
			|||||||
public class DemoUnitTest {
 | 
					public class DemoUnitTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Autowired
 | 
					    @Autowired
 | 
				
			||||||
    private RuoYiConfig ruoYiConfig;
 | 
					    private CaptchaProperties captchaProperties;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
 | 
					    @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testTest() {
 | 
					    public void testTest() {
 | 
				
			||||||
        System.out.println(ruoYiConfig);
 | 
					        System.out.println(captchaProperties);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Disabled
 | 
					    @Disabled
 | 
				
			||||||
    @DisplayName("测试 @Disabled 注解")
 | 
					    @DisplayName("测试 @Disabled 注解")
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testDisabled() {
 | 
					    public void testDisabled() {
 | 
				
			||||||
        System.out.println(ruoYiConfig);
 | 
					        System.out.println(captchaProperties);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
 | 
					    @Timeout(value = 2L, unit = TimeUnit.SECONDS)
 | 
				
			||||||
@@ -37,7 +37,7 @@ public class DemoUnitTest {
 | 
				
			|||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    public void testTimeout() throws InterruptedException {
 | 
					    public void testTimeout() throws InterruptedException {
 | 
				
			||||||
        Thread.sleep(3000);
 | 
					        Thread.sleep(3000);
 | 
				
			||||||
        System.out.println(ruoYiConfig);
 | 
					        System.out.println(captchaProperties);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ruoyi-admin/zhFonts/.uuid
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					3f2ee348-0303-40ca-bf03-03f48d2d2141
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								ruoyi-admin/zhFonts/SIMSUN.TTC
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.dir
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					3
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r
 | 
				
			||||||
							
								
								
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								ruoyi-admin/zhFonts/fonts.scale
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					3
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso10646-1
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-iso8859-1
 | 
				
			||||||
 | 
					SIMSUN.TTC -misc-simsun-medium-r-normal--0-0-0-0-p-0-koi8-r
 | 
				
			||||||
@@ -33,6 +33,7 @@
 | 
				
			|||||||
        <module>ruoyi-common-encrypt</module>
 | 
					        <module>ruoyi-common-encrypt</module>
 | 
				
			||||||
        <module>ruoyi-common-tenant</module>
 | 
					        <module>ruoyi-common-tenant</module>
 | 
				
			||||||
        <module>ruoyi-common-websocket</module>
 | 
					        <module>ruoyi-common-websocket</module>
 | 
				
			||||||
 | 
					        <module>ruoyi-common-sse</module>
 | 
				
			||||||
    </modules>
 | 
					    </modules>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <artifactId>ruoyi-common</artifactId>
 | 
					    <artifactId>ruoyi-common</artifactId>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,7 @@
 | 
				
			|||||||
    </description>
 | 
					    </description>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <properties>
 | 
					    <properties>
 | 
				
			||||||
        <revision>5.1.1</revision>
 | 
					        <revision>5.3.1</revision>
 | 
				
			||||||
    </properties>
 | 
					    </properties>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <dependencyManagement>
 | 
					    <dependencyManagement>
 | 
				
			||||||
@@ -172,6 +172,13 @@
 | 
				
			|||||||
                <version>${revision}</version>
 | 
					                <version>${revision}</version>
 | 
				
			||||||
            </dependency>
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- SSE模块 -->
 | 
				
			||||||
 | 
					            <dependency>
 | 
				
			||||||
 | 
					                <groupId>org.dromara</groupId>
 | 
				
			||||||
 | 
					                <artifactId>ruoyi-common-sse</artifactId>
 | 
				
			||||||
 | 
					                <version>${revision}</version>
 | 
				
			||||||
 | 
					            </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </dependencies>
 | 
					        </dependencies>
 | 
				
			||||||
    </dependencyManagement>
 | 
					    </dependencyManagement>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -66,12 +66,6 @@
 | 
				
			|||||||
            <artifactId>hutool-extra</artifactId>
 | 
					            <artifactId>hutool-extra</artifactId>
 | 
				
			||||||
        </dependency>
 | 
					        </dependency>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <dependency>
 | 
					 | 
				
			||||||
            <groupId>cn.hutool</groupId>
 | 
					 | 
				
			||||||
            <artifactId>hutool-json</artifactId>
 | 
					 | 
				
			||||||
            <scope>provided</scope>
 | 
					 | 
				
			||||||
        </dependency>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <dependency>
 | 
					        <dependency>
 | 
				
			||||||
            <groupId>org.projectlombok</groupId>
 | 
					            <groupId>org.projectlombok</groupId>
 | 
				
			||||||
            <artifactId>lombok</artifactId>
 | 
					            <artifactId>lombok</artifactId>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package org.dromara.common.core.config;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
				
			||||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
 | 
					import org.springframework.context.annotation.EnableAspectJAutoProxy;
 | 
				
			||||||
 | 
					import org.springframework.scheduling.annotation.EnableAsync;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 程序注解配置
 | 
					 * 程序注解配置
 | 
				
			||||||
@@ -9,8 +10,8 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
 | 
				
			|||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@AutoConfiguration
 | 
					@AutoConfiguration
 | 
				
			||||||
// 表示通过aop框架暴露该代理对象,AopContext能够访问
 | 
					@EnableAspectJAutoProxy
 | 
				
			||||||
@EnableAspectJAutoProxy(exposeProxy = true)
 | 
					@EnableAsync(proxyTargetClass = true)
 | 
				
			||||||
public class ApplicationConfig {
 | 
					public class ApplicationConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,18 +5,19 @@ import org.dromara.common.core.exception.ServiceException;
 | 
				
			|||||||
import org.dromara.common.core.utils.SpringUtils;
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
 | 
					import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.core.task.VirtualThreadTaskExecutor;
 | 
				
			||||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
 | 
					import org.springframework.scheduling.annotation.AsyncConfigurer;
 | 
				
			||||||
import org.springframework.scheduling.annotation.EnableAsync;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.concurrent.Executor;
 | 
					import java.util.concurrent.Executor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 异步配置
 | 
					 * 异步配置
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * 如果未使用虚拟线程则生效
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@EnableAsync(proxyTargetClass = true)
 | 
					 | 
				
			||||||
@AutoConfiguration
 | 
					@AutoConfiguration
 | 
				
			||||||
public class AsyncConfig implements AsyncConfigurer {
 | 
					public class AsyncConfig implements AsyncConfigurer {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -25,6 +26,9 @@ public class AsyncConfig implements AsyncConfigurer {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public Executor getAsyncExecutor() {
 | 
					    public Executor getAsyncExecutor() {
 | 
				
			||||||
 | 
					        if(SpringUtils.isVirtual()) {
 | 
				
			||||||
 | 
					            return new VirtualThreadTaskExecutor("async-");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return SpringUtils.getBean("scheduledExecutorService");
 | 
					        return SpringUtils.getBean("scheduledExecutorService");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,33 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.config;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import lombok.Data;
 | 
					 | 
				
			||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
 | 
					 | 
				
			||||||
import org.springframework.stereotype.Component;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 读取项目相关配置
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author Lion Li
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Data
 | 
					 | 
				
			||||||
@Component
 | 
					 | 
				
			||||||
@ConfigurationProperties(prefix = "ruoyi")
 | 
					 | 
				
			||||||
public class RuoYiConfig {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 项目名称
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private String name;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 版本
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private String version;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 版权年份
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private String copyrightYear;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -4,11 +4,13 @@ import jakarta.annotation.PreDestroy;
 | 
				
			|||||||
import lombok.extern.slf4j.Slf4j;
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 | 
					import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 | 
				
			||||||
import org.dromara.common.core.config.properties.ThreadPoolProperties;
 | 
					import org.dromara.common.core.config.properties.ThreadPoolProperties;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.SpringUtils;
 | 
				
			||||||
import org.dromara.common.core.utils.Threads;
 | 
					import org.dromara.common.core.utils.Threads;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
					import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 | 
				
			||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
					import org.springframework.boot.context.properties.EnableConfigurationProperties;
 | 
				
			||||||
import org.springframework.context.annotation.Bean;
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
 | 
					import org.springframework.core.task.VirtualThreadTaskExecutor;
 | 
				
			||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
					import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.concurrent.ScheduledExecutorService;
 | 
					import java.util.concurrent.ScheduledExecutorService;
 | 
				
			||||||
@@ -49,8 +51,15 @@ public class ThreadPoolConfig {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @Bean(name = "scheduledExecutorService")
 | 
					    @Bean(name = "scheduledExecutorService")
 | 
				
			||||||
    protected ScheduledExecutorService scheduledExecutorService() {
 | 
					    protected ScheduledExecutorService scheduledExecutorService() {
 | 
				
			||||||
 | 
					        // daemon 必须为 true
 | 
				
			||||||
 | 
					        BasicThreadFactory.Builder builder = new BasicThreadFactory.Builder().daemon(true);
 | 
				
			||||||
 | 
					        if (SpringUtils.isVirtual()) {
 | 
				
			||||||
 | 
					            builder.namingPattern("virtual-schedule-pool-%d").wrappedFactory(new VirtualThreadTaskExecutor().getVirtualThreadFactory());
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            builder.namingPattern("schedule-pool-%d");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
 | 
					        ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
 | 
				
			||||||
            new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
 | 
					            builder.build(),
 | 
				
			||||||
            new ThreadPoolExecutor.CallerRunsPolicy()) {
 | 
					            new ThreadPoolExecutor.CallerRunsPolicy()) {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            protected void afterExecute(Runnable r, Throwable t) {
 | 
					            protected void afterExecute(Runnable r, Throwable t) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ package org.dromara.common.core.config;
 | 
				
			|||||||
import jakarta.validation.Validator;
 | 
					import jakarta.validation.Validator;
 | 
				
			||||||
import org.hibernate.validator.HibernateValidator;
 | 
					import org.hibernate.validator.HibernateValidator;
 | 
				
			||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
					import org.springframework.boot.autoconfigure.AutoConfiguration;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
 | 
				
			||||||
import org.springframework.context.MessageSource;
 | 
					import org.springframework.context.MessageSource;
 | 
				
			||||||
import org.springframework.context.annotation.Bean;
 | 
					import org.springframework.context.annotation.Bean;
 | 
				
			||||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 | 
					import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
 | 
				
			||||||
@@ -14,11 +15,11 @@ import java.util.Properties;
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@AutoConfiguration
 | 
					@AutoConfiguration(before = ValidationAutoConfiguration.class)
 | 
				
			||||||
public class ValidatorConfig {
 | 
					public class ValidatorConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 配置校验框架 快速返回模式
 | 
					     * 配置校验框架 快速失败模式
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Bean
 | 
					    @Bean
 | 
				
			||||||
    public Validator validator(MessageSource messageSource) {
 | 
					    public Validator validator(MessageSource messageSource) {
 | 
				
			||||||
@@ -28,7 +29,7 @@ public class ValidatorConfig {
 | 
				
			|||||||
            // 设置使用 HibernateValidator 校验器
 | 
					            // 设置使用 HibernateValidator 校验器
 | 
				
			||||||
            factoryBean.setProviderClass(HibernateValidator.class);
 | 
					            factoryBean.setProviderClass(HibernateValidator.class);
 | 
				
			||||||
            Properties properties = new Properties();
 | 
					            Properties properties = new Properties();
 | 
				
			||||||
            // 设置 快速异常返回
 | 
					            // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误
 | 
				
			||||||
            properties.setProperty("hibernate.validator.fail_fast", "true");
 | 
					            properties.setProperty("hibernate.validator.fail_fast", "true");
 | 
				
			||||||
            factoryBean.setValidationProperties(properties);
 | 
					            factoryBean.setValidationProperties(properties);
 | 
				
			||||||
            // 加载配置
 | 
					            // 加载配置
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,4 +22,9 @@ public interface CacheConstants {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_DICT_KEY = "sys_dict:";
 | 
					    String SYS_DICT_KEY = "sys_dict:";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 登录账户密码错误次数 redis key
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,14 @@ package org.dromara.common.core.constant;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * 缓存组名称常量
 | 
					 * 缓存组名称常量
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
 | 
					 * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * ttl 过期时间 如果设置为0则不过期 默认为0
 | 
					 * ttl 过期时间 如果设置为0则不过期 默认为0
 | 
				
			||||||
 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
 | 
					 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
 | 
				
			||||||
 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
 | 
					 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
 | 
				
			||||||
 | 
					 * local 默认开启本地缓存为1 关闭本地缓存为0
 | 
				
			||||||
 * <p>
 | 
					 * <p>
 | 
				
			||||||
 * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
 | 
					 * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500、test#1h#0#500#0
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -30,16 +31,31 @@ public interface CacheNames {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_DICT = "sys_dict";
 | 
					    String SYS_DICT = "sys_dict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 数据字典类型
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SYS_DICT_TYPE = "sys_dict_type";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 租户
 | 
					     * 租户
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
 | 
					    String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 客户端
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 用户账户
 | 
					     * 用户账户
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_USER_NAME = "sys_user_name#30d";
 | 
					    String SYS_USER_NAME = "sys_user_name#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SYS_NICKNAME = "sys_nickname#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 部门
 | 
					     * 部门
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -50,10 +66,20 @@ public interface CacheNames {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_OSS = "sys_oss#30d";
 | 
					    String SYS_OSS = "sys_oss#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 角色自定义权限
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门及以下权限
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * OSS配置
 | 
					     * OSS配置
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    String SYS_OSS_CONFIG = "sys_oss_config";
 | 
					    String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 在线用户
 | 
					     * 在线用户
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,12 +68,7 @@ public interface Constants {
 | 
				
			|||||||
    Integer CAPTCHA_EXPIRATION = 2;
 | 
					    Integer CAPTCHA_EXPIRATION = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 令牌
 | 
					     * 顶级父级id
 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String TOKEN = "token";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 顶级部门id
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    Long TOP_PARENT_ID = 0L;
 | 
					    Long TOP_PARENT_ID = 0L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,11 +27,6 @@ public interface GlobalConstants {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
 | 
					    String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 登录账户密码错误次数 redis key
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String PWD_ERR_CNT_KEY = GLOBAL_REDIS_KEY + "pwd_err_cnt:";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 三方认证 redis key
 | 
					     * 三方认证 redis key
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.constant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.lang.RegexPool;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 常用正则表达式字符串
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Feng
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface RegexConstants extends RegexPool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 权限标识必须符合以下格式:
 | 
				
			||||||
 | 
					     * 1. 标准格式:xxx:yyy:zzz
 | 
				
			||||||
 | 
					     * - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*`
 | 
				
			||||||
 | 
					     * - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*`
 | 
				
			||||||
 | 
					     * - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*`
 | 
				
			||||||
 | 
					     * 2. 允许空字符串(""),表示没有权限标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 身份证号码(后6位)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * QQ号码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 邮政编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String POSTAL_CODE = "^[1-9]\\d{5}$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 注册账号
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通用状态(0表示正常,1表示停用)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String STATUS = "^[01]$";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,80 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.constant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 系统常量信息
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Lion Li
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface SystemConstants {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 正常状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String NORMAL = "0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 异常状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String DISABLE = "1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否为系统默认(是)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String YES = "Y";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否为系统默认(否)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String NO = "N";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否菜单外链(是)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String YES_FRAME = "0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否菜单外链(否)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String NO_FRAME = "1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 菜单类型(目录)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String TYPE_DIR = "M";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 菜单类型(菜单)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String TYPE_MENU = "C";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 菜单类型(按钮)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String TYPE_BUTTON = "F";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Layout组件标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String LAYOUT = "Layout";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ParentView组件标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String PARENT_VIEW = "ParentView";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * InnerLink组件标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String INNER_LINK = "InnerLink";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 超级管理员ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Long SUPER_ADMIN_ID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根部门祖级列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String ROOT_DEPT_ANCESTORS = "0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -7,16 +7,6 @@ package org.dromara.common.core.constant;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
public interface TenantConstants {
 | 
					public interface TenantConstants {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 租户正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 租户封禁状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 超级管理员ID
 | 
					     * 超级管理员ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,142 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.constant;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 用户常量信息
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author ruoyi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public interface UserConstants {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 平台内系统用户的唯一标志
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String SYS_USER = "SYS_USER";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 异常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String EXCEPTION = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 用户正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String USER_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 用户封禁状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String USER_DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 角色正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String ROLE_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 角色封禁状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String ROLE_DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 部门正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String DEPT_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 部门停用状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String DEPT_DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 岗位正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String POST_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 岗位停用状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String POST_DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 字典正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String DICT_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 是否为系统默认(是)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String YES = "Y";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 是否菜单外链(是)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String YES_FRAME = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 是否菜单外链(否)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String NO_FRAME = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 菜单正常状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String MENU_NORMAL = "0";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 菜单停用状态
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String MENU_DISABLE = "1";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 菜单类型(目录)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String TYPE_DIR = "M";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 菜单类型(菜单)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String TYPE_MENU = "C";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 菜单类型(按钮)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String TYPE_BUTTON = "F";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Layout组件标识
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String LAYOUT = "Layout";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * ParentView组件标识
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String PARENT_VIEW = "ParentView";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * InnerLink组件标识
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    String INNER_LINK = "InnerLink";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 用户名长度限制
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    int USERNAME_MIN_LENGTH = 2;
 | 
					 | 
				
			||||||
    int USERNAME_MAX_LENGTH = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 密码长度限制
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    int PASSWORD_MIN_LENGTH = 5;
 | 
					 | 
				
			||||||
    int PASSWORD_MAX_LENGTH = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 超级管理员ID
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    Long SUPER_ADMIN_ID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 办理任务请求对象
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class CompleteTaskDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 任务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long taskId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 附件id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String fileId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 抄送人员
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<FlowCopyDTO> flowCopyList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 消息类型
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<String> messageType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 办理意见
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String message;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 消息通知
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String notice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程变量
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Map<String, Object> variables;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 扩展变量(此处为逗号分隔的ossId)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String ext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<String, Object> getVariables() {
 | 
				
			||||||
 | 
					        if (variables == null) {
 | 
				
			||||||
 | 
					            return new HashMap<>(16);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
 | 
				
			||||||
 | 
					        return variables;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 部门
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class DeptDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long deptId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 父部门ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long parentId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String deptName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 字典数据DTO
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class DictDataDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典标签
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String dictLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典键值
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String dictValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 是否默认(Y是 N否)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String isDefault;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 备注
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String remark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 字典类型DTO
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class DictTypeDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典主键
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long dictId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String dictName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典类型
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String dictType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 备注
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String remark;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 抄送
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class FlowCopyDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String userName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * OSS对象
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Lion Li
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class OssDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 对象存储主键
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long ossId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 文件名
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String fileName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 原名
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String originalName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 文件后缀名
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String fileSuffix;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * URL地址
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String url;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 岗位
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class PostDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 岗位ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long postId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long deptId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 岗位编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String postCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 岗位名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String postName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 岗位类别编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String postCategory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,6 +3,7 @@ package org.dromara.common.core.domain.dto;
 | 
				
			|||||||
import lombok.Data;
 | 
					import lombok.Data;
 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
import java.io.Serializable;
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -15,6 +16,9 @@ import java.io.Serializable;
 | 
				
			|||||||
@NoArgsConstructor
 | 
					@NoArgsConstructor
 | 
				
			||||||
public class RoleDTO implements Serializable {
 | 
					public class RoleDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 角色ID
 | 
					     * 角色ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -31,7 +35,7 @@ public class RoleDTO implements Serializable {
 | 
				
			|||||||
    private String roleKey;
 | 
					    private String roleKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限)
 | 
					     * 数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限 6:部门及以下或本人数据权限)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private String dataScope;
 | 
					    private String dataScope;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 启动流程对象
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class StartProcessDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 业务唯一值id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String businessId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程定义编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String flowCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Map<String, Object> variables;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Map<String, Object> getVariables() {
 | 
				
			||||||
 | 
					        if (variables == null) {
 | 
				
			||||||
 | 
					            return new HashMap<>(16);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
 | 
				
			||||||
 | 
					        return variables;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 启动流程返回对象
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Lion Li
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class StartProcessReturnDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程实例id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long processInstanceId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 任务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long taskId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,101 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 任务受让人
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class TaskAssigneeDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 总大小
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long total = 0L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<TaskHandler> list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public TaskAssigneeDTO(Long total, List<TaskHandler> list) {
 | 
				
			||||||
 | 
					        this.total = total;
 | 
				
			||||||
 | 
					        this.list = list;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将源列表转换为 TaskHandler 列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <T>              通用类型
 | 
				
			||||||
 | 
					     * @param sourceList       待转换的源列表
 | 
				
			||||||
 | 
					     * @param storageId        提取 storageId 的函数
 | 
				
			||||||
 | 
					     * @param handlerCode      提取 handlerCode 的函数
 | 
				
			||||||
 | 
					     * @param handlerName      提取 handlerName 的函数
 | 
				
			||||||
 | 
					     * @param groupName        提取 groupName 的函数
 | 
				
			||||||
 | 
					     * @param createTimeMapper 提取 createTime 的函数
 | 
				
			||||||
 | 
					     * @return 转换后的 TaskHandler 列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T> List<TaskHandler> convertToHandlerList(
 | 
				
			||||||
 | 
					        List<T> sourceList,
 | 
				
			||||||
 | 
					        Function<T, Long> storageId,
 | 
				
			||||||
 | 
					        Function<T, String> handlerCode,
 | 
				
			||||||
 | 
					        Function<T, String> handlerName,
 | 
				
			||||||
 | 
					        Function<T, Long> groupName,
 | 
				
			||||||
 | 
					        Function<T, Date> createTimeMapper) {
 | 
				
			||||||
 | 
					        return sourceList.stream()
 | 
				
			||||||
 | 
					            .map(item -> new TaskHandler(
 | 
				
			||||||
 | 
					                String.valueOf(storageId.apply(item)),
 | 
				
			||||||
 | 
					                handlerCode.apply(item),
 | 
				
			||||||
 | 
					                handlerName.apply(item),
 | 
				
			||||||
 | 
					                groupName != null ? String.valueOf(groupName.apply(item)) : null,
 | 
				
			||||||
 | 
					                createTimeMapper.apply(item)
 | 
				
			||||||
 | 
					            )).collect(Collectors.toList());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Data
 | 
				
			||||||
 | 
					    @NoArgsConstructor
 | 
				
			||||||
 | 
					    @AllArgsConstructor
 | 
				
			||||||
 | 
					    public static class TaskHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 主键
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private String storageId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 权限编码
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private String handlerCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 权限名称
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private String handlerName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 权限分组
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private String groupName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * 创建时间
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        private Date createTime;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.dto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 用户
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Michelle.Chung
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class UserDTO implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long userId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long deptId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户账号
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String userName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户昵称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String nickName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户类型(sys_user系统用户)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String userType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户邮箱
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String email;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 手机号码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String phonenumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户性别(0男 1女 2未知)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String sex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 帐号状态(0正常 1停用)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 创建时间
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Date createTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 流程创建任务监听
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class ProcessCreateTaskEvent implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 租户ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String tenantId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程定义编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String flowCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Integer nodeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程节点编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String nodeCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程节点名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String nodeName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 任务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Long taskId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 业务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String businessId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 删除流程监听
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class ProcessDeleteEvent implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 租户ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String tenantId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程定义编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String flowCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 业务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String businessId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.event;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 总体流程监听
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					public class ProcessEvent implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 租户ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String tenantId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程定义编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String flowCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 业务id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String businessId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Integer nodeType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程节点编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String nodeCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程节点名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String nodeName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 流程状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 办理参数
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Map<String, Object> params;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 当为true时为申请人节点办理
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Boolean submit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
package org.dromara.common.core.domain.model;
 | 
					package org.dromara.common.core.domain.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.dromara.common.core.domain.dto.RoleDTO;
 | 
					 | 
				
			||||||
import lombok.Data;
 | 
					import lombok.Data;
 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.PostDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.RoleDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
import java.io.Serializable;
 | 
					import java.io.Serializable;
 | 
				
			||||||
@@ -14,7 +15,6 @@ import java.util.Set;
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					 | 
				
			||||||
@Data
 | 
					@Data
 | 
				
			||||||
@NoArgsConstructor
 | 
					@NoArgsConstructor
 | 
				
			||||||
public class LoginUser implements Serializable {
 | 
					public class LoginUser implements Serializable {
 | 
				
			||||||
@@ -37,6 +37,11 @@ public class LoginUser implements Serializable {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private Long deptId;
 | 
					    private Long deptId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 部门类别编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String deptCategory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 部门名
 | 
					     * 部门名
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -107,6 +112,11 @@ public class LoginUser implements Serializable {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private List<RoleDTO> roles;
 | 
					    private List<RoleDTO> roles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 岗位对象
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private List<PostDTO> posts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 数据权限 当前角色ID
 | 
					     * 数据权限 当前角色ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,6 @@ import lombok.Data;
 | 
				
			|||||||
import lombok.EqualsAndHashCode;
 | 
					import lombok.EqualsAndHashCode;
 | 
				
			||||||
import org.hibernate.validator.constraints.Length;
 | 
					import org.hibernate.validator.constraints.Length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.dromara.common.core.constant.UserConstants.*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 密码登录对象
 | 
					 * 密码登录对象
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -20,14 +18,14 @@ public class PasswordLoginBody extends LoginBody {
 | 
				
			|||||||
     * 用户名
 | 
					     * 用户名
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @NotBlank(message = "{user.username.not.blank}")
 | 
					    @NotBlank(message = "{user.username.not.blank}")
 | 
				
			||||||
    @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
 | 
					    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
 | 
				
			||||||
    private String username;
 | 
					    private String username;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 用户密码
 | 
					     * 用户密码
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @NotBlank(message = "{user.password.not.blank}")
 | 
					    @NotBlank(message = "{user.password.not.blank}")
 | 
				
			||||||
    @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
 | 
					    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
 | 
				
			||||||
    private String password;
 | 
					    private String password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,8 +5,6 @@ import lombok.Data;
 | 
				
			|||||||
import lombok.EqualsAndHashCode;
 | 
					import lombok.EqualsAndHashCode;
 | 
				
			||||||
import org.hibernate.validator.constraints.Length;
 | 
					import org.hibernate.validator.constraints.Length;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static org.dromara.common.core.constant.UserConstants.*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 用户注册对象
 | 
					 * 用户注册对象
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -20,14 +18,14 @@ public class RegisterBody extends LoginBody {
 | 
				
			|||||||
     * 用户名
 | 
					     * 用户名
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @NotBlank(message = "{user.username.not.blank}")
 | 
					    @NotBlank(message = "{user.username.not.blank}")
 | 
				
			||||||
    @Length(min = USERNAME_MIN_LENGTH, max = USERNAME_MAX_LENGTH, message = "{user.username.length.valid}")
 | 
					    @Length(min = 2, max = 30, message = "{user.username.length.valid}")
 | 
				
			||||||
    private String username;
 | 
					    private String username;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 用户密码
 | 
					     * 用户密码
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @NotBlank(message = "{user.password.not.blank}")
 | 
					    @NotBlank(message = "{user.password.not.blank}")
 | 
				
			||||||
    @Length(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH, message = "{user.password.length.valid}")
 | 
					    @Length(min = 5, max = 30, message = "{user.password.length.valid}")
 | 
				
			||||||
    private String password;
 | 
					    private String password;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String userType;
 | 
					    private String userType;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.domain.model;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.Data;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					import java.io.Serializable;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 任务受让人
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Data
 | 
				
			||||||
 | 
					@NoArgsConstructor
 | 
				
			||||||
 | 
					public class TaskAssigneeBody implements Serializable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Serial
 | 
				
			||||||
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 权限编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String handlerCode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 权限名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String handlerName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 权限分组
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String groupId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 开始时间
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String beginTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 结束时间
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String endTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 当前页
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Integer pageNum = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 每页显示条数
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Integer pageSize = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,215 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.enums;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.Getter;
 | 
				
			||||||
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.StringUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 业务状态枚举
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Getter
 | 
				
			||||||
 | 
					@AllArgsConstructor
 | 
				
			||||||
 | 
					public enum BusinessStatusEnum {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 已撤销
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    CANCEL("cancel", "已撤销"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 草稿
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    DRAFT("draft", "草稿"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 待审核
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    WAITING("waiting", "待审核"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 已完成
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    FINISH("finish", "已完成"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 已作废
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    INVALID("invalid", "已作废"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 已退回
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    BACK("back", "已退回"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 已终止
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    TERMINATION("termination", "已终止");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private final String status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 描述
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private final String desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final Map<String, BusinessStatusEnum> STATUS_MAP = Arrays.stream(BusinessStatusEnum.values())
 | 
				
			||||||
 | 
					        .collect(Collectors.toConcurrentMap(BusinessStatusEnum::getStatus, Function.identity()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据状态获取对应的 BusinessStatusEnum 枚举
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 业务状态码
 | 
				
			||||||
 | 
					     * @return 对应的 BusinessStatusEnum 枚举,如果找不到则返回 null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static BusinessStatusEnum getByStatus(String status) {
 | 
				
			||||||
 | 
					        // 使用 STATUS_MAP 获取对应的枚举,若找不到则返回 null
 | 
				
			||||||
 | 
					        return STATUS_MAP.get(status);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据状态获取对应的业务状态描述信息
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 业务状态码
 | 
				
			||||||
 | 
					     * @return 返回业务状态描述,若状态码为空或未找到对应的枚举,返回空字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String findByStatus(String status) {
 | 
				
			||||||
 | 
					        if (StringUtils.isBlank(status)) {
 | 
				
			||||||
 | 
					            return StrUtil.EMPTY;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
 | 
				
			||||||
 | 
					        return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 判断是否为指定的状态之一:草稿、已撤销或已退回
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 要检查的状态
 | 
				
			||||||
 | 
					     * @return 如果状态为草稿、已撤销或已退回之一,则返回 true;否则返回 false
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean isDraftOrCancelOrBack(String status) {
 | 
				
			||||||
 | 
					        return DRAFT.status.equals(status) || CANCEL.status.equals(status) || BACK.status.equals(status);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 判断是否为撤销,退回,作废,终止
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status status
 | 
				
			||||||
 | 
					     * @return 结果
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean initialState(String status) {
 | 
				
			||||||
 | 
					        return CANCEL.status.equals(status) || BACK.status.equals(status) || INVALID.status.equals(status) || TERMINATION.status.equals(status);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取运行中的实例状态列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 包含运行中实例状态的不可变列表
 | 
				
			||||||
 | 
					     * (包含 DRAFT、WAITING、BACK 和 CANCEL 状态)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<String> runningStatus() {
 | 
				
			||||||
 | 
					        return Arrays.asList(DRAFT.status, WAITING.status, BACK.status, CANCEL.status);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取结束实例的状态列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 包含结束实例状态的不可变列表
 | 
				
			||||||
 | 
					     * (包含 FINISH、INVALID 和 TERMINATION 状态)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static List<String> finishStatus() {
 | 
				
			||||||
 | 
					        return Arrays.asList(FINISH.status, INVALID.status, TERMINATION.status);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 启动流程校验
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void checkStartStatus(String status) {
 | 
				
			||||||
 | 
					        if (WAITING.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已提交过申请,正在审批中!");
 | 
				
			||||||
 | 
					        } else if (FINISH.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已完成申请!");
 | 
				
			||||||
 | 
					        } else if (INVALID.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已作废!");
 | 
				
			||||||
 | 
					        } else if (TERMINATION.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已终止!");
 | 
				
			||||||
 | 
					        } else if (StringUtils.isBlank(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("流程状态为空!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 撤销流程校验
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void checkCancelStatus(String status) {
 | 
				
			||||||
 | 
					        if (CANCEL.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已撤销!");
 | 
				
			||||||
 | 
					        } else if (FINISH.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已完成申请!");
 | 
				
			||||||
 | 
					        } else if (INVALID.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已作废!");
 | 
				
			||||||
 | 
					        } else if (TERMINATION.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已终止!");
 | 
				
			||||||
 | 
					        } else if (BACK.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已退回!");
 | 
				
			||||||
 | 
					        } else if (StringUtils.isBlank(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("流程状态为空!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 驳回流程校验
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void checkBackStatus(String status) {
 | 
				
			||||||
 | 
					        if (BACK.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已退回!");
 | 
				
			||||||
 | 
					        } else if (FINISH.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已完成申请!");
 | 
				
			||||||
 | 
					        } else if (INVALID.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已作废!");
 | 
				
			||||||
 | 
					        } else if (TERMINATION.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已终止!");
 | 
				
			||||||
 | 
					        } else if (CANCEL.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已撤销!");
 | 
				
			||||||
 | 
					        } else if (StringUtils.isBlank(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("流程状态为空!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 作废,终止流程校验
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param status 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void checkInvalidStatus(String status) {
 | 
				
			||||||
 | 
					        if (FINISH.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已完成申请!");
 | 
				
			||||||
 | 
					        } else if (INVALID.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已作废!");
 | 
				
			||||||
 | 
					        } else if (TERMINATION.getStatus().equals(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("该单据已终止!");
 | 
				
			||||||
 | 
					        } else if (StringUtils.isBlank(status)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("流程状态为空!");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,7 +5,6 @@ import lombok.Getter;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 设备类型
 | 
					 * 设备类型
 | 
				
			||||||
 * 针对一套 用户体系
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -29,9 +28,12 @@ public enum DeviceType {
 | 
				
			|||||||
    XCX("xcx"),
 | 
					    XCX("xcx"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * social第三方端
 | 
					     * 第三方社交登录平台
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    SOCIAL("social");
 | 
					    SOCIAL("social");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设备标识
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private final String device;
 | 
					    private final String device;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.enums;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.Getter;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.StringUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 日期格式
 | 
				
			||||||
 | 
					 * "yyyy":4位数的年份,例如:2023年表示为"2023"。
 | 
				
			||||||
 | 
					 * "yy":2位数的年份,例如:2023年表示为"23"。
 | 
				
			||||||
 | 
					 * "MM":2位数的月份,取值范围为01到12,例如:7月表示为"07"。
 | 
				
			||||||
 | 
					 * "M":不带前导零的月份,取值范围为1到12,例如:7月表示为"7"。
 | 
				
			||||||
 | 
					 * "dd":2位数的日期,取值范围为01到31,例如:22日表示为"22"。
 | 
				
			||||||
 | 
					 * "d":不带前导零的日期,取值范围为1到31,例如:22日表示为"22"。
 | 
				
			||||||
 | 
					 * "EEEE":星期的全名,例如:星期三表示为"Wednesday"。
 | 
				
			||||||
 | 
					 * "E":星期的缩写,例如:星期三表示为"Wed"。
 | 
				
			||||||
 | 
					 * "DDD" 或 "D":一年中的第几天,取值范围为001到366,例如:第200天表示为"200"。
 | 
				
			||||||
 | 
					 * 时间格式
 | 
				
			||||||
 | 
					 * "HH":24小时制的小时数,取值范围为00到23,例如:下午5点表示为"17"。
 | 
				
			||||||
 | 
					 * "hh":12小时制的小时数,取值范围为01到12,例如:下午5点表示为"05"。
 | 
				
			||||||
 | 
					 * "mm":分钟数,取值范围为00到59,例如:30分钟表示为"30"。
 | 
				
			||||||
 | 
					 * "ss":秒数,取值范围为00到59,例如:45秒表示为"45"。
 | 
				
			||||||
 | 
					 * "SSS":毫秒数,取值范围为000到999,例如:123毫秒表示为"123"。
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 日期格式与时间格式枚举
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Getter
 | 
				
			||||||
 | 
					@AllArgsConstructor
 | 
				
			||||||
 | 
					public enum FormatsType {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如:2023年表示为"23"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YY("yy"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如:2023年表示为"2023"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY("yyyy"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例例如,2023年7月可以表示为 "2023-07"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM("yyyy-MM"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,日期 "2023年7月22日" 可以表示为 "2023-07-22"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD("yyyy-MM-dd"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023-07-22 15:30"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM("yyyy-MM-dd HH:mm"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023-07-22 15:30:45"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM_SS("yyyy-MM-dd HH:mm:ss"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如:下午3点30分45秒,表示为 "15:30:45"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    HH_MM_SS("HH:mm:ss"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例例如,2023年7月可以表示为 "2023/07"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_SLASH("yyyy/MM"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,日期 "2023年7月22日" 可以表示为 "2023/07/22"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_SLASH("yyyy/MM/dd"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM_SLASH("yyyy/MM/dd HH:mm"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023/07/22 15:30:45"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM_SS_SLASH("yyyy/MM/dd HH:mm:ss"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例例如,2023年7月可以表示为 "2023.07"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DOT("yyyy.MM"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,日期 "2023年7月22日" 可以表示为 "2023.07.22"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_DOT("yyyy.MM.dd"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分",则可以表示为 "2023.07.22 15:30"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM_DOT("yyyy.MM.dd HH:mm"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,当前时间如果是 "2023年7月22日下午3点30分45秒",则可以表示为 "2023.07.22 15:30:45"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYY_MM_DD_HH_MM_SS_DOT("yyyy.MM.dd HH:mm:ss"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,2023年7月可以表示为 "202307"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYYMM("yyyyMM"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,2023年7月22日可以表示为 "20230722"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYYMMDD("yyyyMMdd"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,2023年7月22日下午3点可以表示为 "2023072215"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYYMMDDHH("yyyyMMddHH"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,2023年7月22日下午3点30分可以表示为 "202307221530"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYYMMDDHHMM("yyyyMMddHHmm"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 例如,2023年7月22日下午3点30分45秒可以表示为 "20230722153045"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    YYYYMMDDHHMMSS("yyyyMMddHHmmss");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 时间格式
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private final String timeFormat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static FormatsType getFormatsType(String str) {
 | 
				
			||||||
 | 
					        for (FormatsType value : values()) {
 | 
				
			||||||
 | 
					            if (StringUtils.contains(str, value.getTimeFormat())) {
 | 
				
			||||||
 | 
					                return value;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new RuntimeException("'FormatsType' not found By " + str);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,30 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.enums;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import lombok.AllArgsConstructor;
 | 
					 | 
				
			||||||
import lombok.Getter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 用户状态
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author LionLi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
@Getter
 | 
					 | 
				
			||||||
@AllArgsConstructor
 | 
					 | 
				
			||||||
public enum TenantStatus {
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 正常
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    OK("0", "正常"),
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 停用
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    DISABLE("1", "停用"),
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 删除
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    DELETED("2", "删除");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final String code;
 | 
					 | 
				
			||||||
    private final String info;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,12 +1,11 @@
 | 
				
			|||||||
package org.dromara.common.core.enums;
 | 
					package org.dromara.common.core.enums;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.dromara.common.core.utils.StringUtils;
 | 
					 | 
				
			||||||
import lombok.AllArgsConstructor;
 | 
					import lombok.AllArgsConstructor;
 | 
				
			||||||
import lombok.Getter;
 | 
					import lombok.Getter;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.StringUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 设备类型
 | 
					 * 用户类型
 | 
				
			||||||
 * 针对多套 用户体系
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -15,15 +14,18 @@ import lombok.Getter;
 | 
				
			|||||||
public enum UserType {
 | 
					public enum UserType {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * pc端
 | 
					     * 后台系统用户
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    SYS_USER("sys_user"),
 | 
					    SYS_USER("sys_user"),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * app端
 | 
					     * 移动客户端用户
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    APP_USER("app_user");
 | 
					    APP_USER("app_user");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 用户类型标识(用于 token、权限识别等)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private final String userType;
 | 
					    private final String userType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static UserType getUserType(String str) {
 | 
					    public static UserType getUserType(String str) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.exception;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.Serial;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 演示模式异常
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author ruoyi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class DemoModeException extends RuntimeException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Serial
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public DemoModeException() {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,9 +1,6 @@
 | 
				
			|||||||
package org.dromara.common.core.exception;
 | 
					package org.dromara.common.core.exception;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.AllArgsConstructor;
 | 
					import lombok.*;
 | 
				
			||||||
import lombok.Data;
 | 
					 | 
				
			||||||
import lombok.EqualsAndHashCode;
 | 
					 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,19 +42,11 @@ public final class ServiceException extends RuntimeException {
 | 
				
			|||||||
        this.code = code;
 | 
					        this.code = code;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getDetailMessage() {
 | 
					 | 
				
			||||||
        return detailMessage;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String getMessage() {
 | 
					    public String getMessage() {
 | 
				
			||||||
        return message;
 | 
					        return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public Integer getCode() {
 | 
					 | 
				
			||||||
        return code;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public ServiceException setMessage(String message) {
 | 
					    public ServiceException setMessage(String message) {
 | 
				
			||||||
        this.message = message;
 | 
					        this.message = message;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,19 +8,24 @@ import lombok.NoArgsConstructor;
 | 
				
			|||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 全局异常
 | 
					 * sse 特制异常
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author ruoyi
 | 
					 * @author LionLi
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@Data
 | 
					@Data
 | 
				
			||||||
@EqualsAndHashCode(callSuper = true)
 | 
					@EqualsAndHashCode(callSuper = true)
 | 
				
			||||||
@NoArgsConstructor
 | 
					@NoArgsConstructor
 | 
				
			||||||
@AllArgsConstructor
 | 
					@AllArgsConstructor
 | 
				
			||||||
public class GlobalException extends RuntimeException {
 | 
					public final class SseException extends RuntimeException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Serial
 | 
					    @Serial
 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 错误码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Integer code;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 错误提示
 | 
					     * 错误提示
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -31,17 +36,13 @@ public class GlobalException extends RuntimeException {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private String detailMessage;
 | 
					    private String detailMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GlobalException(String message) {
 | 
					    public SseException(String message) {
 | 
				
			||||||
        this.message = message;
 | 
					        this.message = message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public String getDetailMessage() {
 | 
					    public SseException(String message, Integer code) {
 | 
				
			||||||
        return detailMessage;
 | 
					        this.message = message;
 | 
				
			||||||
    }
 | 
					        this.code = code;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public GlobalException setDetailMessage(String detailMessage) {
 | 
					 | 
				
			||||||
        this.detailMessage = detailMessage;
 | 
					 | 
				
			||||||
        return this;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -49,8 +50,13 @@ public class GlobalException extends RuntimeException {
 | 
				
			|||||||
        return message;
 | 
					        return message;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public GlobalException setMessage(String message) {
 | 
					    public SseException setMessage(String message) {
 | 
				
			||||||
        this.message = message;
 | 
					        this.message = message;
 | 
				
			||||||
        return this;
 | 
					        return this;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SseException setDetailMessage(String detailMessage) {
 | 
				
			||||||
 | 
					        this.detailMessage = detailMessage;
 | 
				
			||||||
 | 
					        return this;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,26 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.exception;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.Serial;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 工具类异常
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author ruoyi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class UtilException extends RuntimeException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Serial
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 8247610319171014183L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public UtilException(Throwable e) {
 | 
					 | 
				
			||||||
        super(e.getMessage(), e);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public UtilException(String message) {
 | 
					 | 
				
			||||||
        super(message);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public UtilException(String message, Throwable throwable) {
 | 
					 | 
				
			||||||
        super(message, throwable);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,18 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.exception.user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.Serial;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 用户密码不正确或不符合规范异常类
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author ruoyi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class UserPasswordNotMatchException extends UserException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Serial
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public UserPasswordNotMatchException() {
 | 
					 | 
				
			||||||
        super("user.password.not.match");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,19 +0,0 @@
 | 
				
			|||||||
package org.dromara.common.core.exception.user;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.Serial;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * 用户错误最大次数异常类
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @author ruoyi
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class UserPasswordRetryLimitExceedException extends UserException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Serial
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) {
 | 
					 | 
				
			||||||
        super("user.password.retry.limit.exceed", retryLimitCount, lockTime);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.factory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.lang.PatternPool;
 | 
				
			||||||
 | 
					import org.dromara.common.core.constant.RegexConstants;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 正则表达式模式池工厂
 | 
				
			||||||
 | 
					 * <p>初始化的时候将正则表达式加入缓存池当中</p>
 | 
				
			||||||
 | 
					 * <p>提高正则表达式的性能,避免重复编译相同的正则表达式</p>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author 21001
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class RegexPatternPoolFactory extends PatternPool {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern DICTIONARY_TYPE = get(RegexConstants.DICTIONARY_TYPE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 身份证号码(后6位)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern ID_CARD_LAST_6 = get(RegexConstants.ID_CARD_LAST_6);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * QQ号码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern QQ_NUMBER = get(RegexConstants.QQ_NUMBER);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 邮政编码
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern POSTAL_CODE = get(RegexConstants.POSTAL_CODE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 注册账号
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern ACCOUNT = get(RegexConstants.ACCOUNT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 密码:包含至少8个字符,包括大写字母、小写字母、数字和特殊字符
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern PASSWORD = get(RegexConstants.PASSWORD);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通用状态(0表示正常,1表示停用)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static final Pattern STATUS = get(RegexConstants.STATUS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
package org.dromara.common.core.service;
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.DeptDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 通用 部门服务
 | 
					 * 通用 部门服务
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -15,4 +19,19 @@ public interface DeptService {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String selectDeptNameByIds(String deptIds);
 | 
					    String selectDeptNameByIds(String deptIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据部门ID查询部门负责人
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param deptId 部门ID,用于指定需要查询的部门
 | 
				
			||||||
 | 
					     * @return 返回该部门的负责人ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Long selectDeptLeaderById(Long deptId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 查询部门
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 部门列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<DeptDTO> selectDeptsByList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
package org.dromara.common.core.service;
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.DictDataDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.DictTypeDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -64,4 +68,20 @@ public interface DictService {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    Map<String, String> getAllDictByDictType(String dictType);
 | 
					    Map<String, String> getAllDictByDictType(String dictType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据字典类型查询详细信息
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dictType 字典类型
 | 
				
			||||||
 | 
					     * @return 字典类型详细信息
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    DictTypeDTO getDictType(String dictType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据字典类型查询字典数据列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param dictType 字典类型
 | 
				
			||||||
 | 
					     * @return 字典数据列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<DictDataDTO> getDictData(String dictType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,9 @@
 | 
				
			|||||||
package org.dromara.common.core.service;
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.OssDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 通用 OSS服务
 | 
					 * 通用 OSS服务
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -15,4 +19,11 @@ public interface OssService {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String selectUrlByIds(String ossIds);
 | 
					    String selectUrlByIds(String ossIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过ossId查询列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ossIds ossId串逗号分隔
 | 
				
			||||||
 | 
					     * @return 列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<OssDTO> selectByIds(String ossIds);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 通用 岗位服务
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface PostService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 通用 角色服务
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author AprilWind
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface RoleService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.model.TaskAssigneeBody;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 工作流设计器获取任务执行人
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author Lion Li
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface TaskAssigneeService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 查询角色并返回任务指派的列表,支持分页
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskQuery 查询条件
 | 
				
			||||||
 | 
					     * @return 办理人
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    TaskAssigneeDTO selectRolesByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 查询岗位并返回任务指派的列表,支持分页
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskQuery 查询条件
 | 
				
			||||||
 | 
					     * @return 办理人
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    TaskAssigneeDTO selectPostsByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 查询部门并返回任务指派的列表,支持分页
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskQuery 查询条件
 | 
				
			||||||
 | 
					     * @return 办理人
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    TaskAssigneeDTO selectDeptsByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 查询用户并返回任务指派的列表,支持分页
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskQuery 查询条件
 | 
				
			||||||
 | 
					     * @return 办理人
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    TaskAssigneeDTO selectUsersByTaskAssigneeList(TaskAssigneeBody taskQuery);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,5 +1,10 @@
 | 
				
			|||||||
package org.dromara.common.core.service;
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.UserDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 通用 用户服务
 | 
					 * 通用 用户服务
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@@ -15,4 +20,108 @@ public interface UserService {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    String selectUserNameById(Long userId);
 | 
					    String selectUserNameById(Long userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过用户ID查询用户账户
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userId 用户ID
 | 
				
			||||||
 | 
					     * @return 用户名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String selectNicknameById(Long userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过用户ID查询用户账户
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userIds 用户ID 多个用逗号隔开
 | 
				
			||||||
 | 
					     * @return 用户名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String selectNicknameByIds(String userIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过用户ID查询用户手机号
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userId 用户id
 | 
				
			||||||
 | 
					     * @return 用户手机号
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String selectPhonenumberById(Long userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过用户ID查询用户邮箱
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userId 用户id
 | 
				
			||||||
 | 
					     * @return 用户邮箱
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String selectEmailById(Long userId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过用户ID查询用户列表
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userIds 用户ids
 | 
				
			||||||
 | 
					     * @return 用户列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<UserDTO> selectListByIds(List<Long> userIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过角色ID查询用户ID
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param roleIds 角色ids
 | 
				
			||||||
 | 
					     * @return 用户ids
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过角色ID查询用户
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param roleIds 角色ids
 | 
				
			||||||
 | 
					     * @return 用户
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<UserDTO> selectUsersByRoleIds(List<Long> roleIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过部门ID查询用户
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param deptIds 部门ids
 | 
				
			||||||
 | 
					     * @return 用户
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 通过岗位ID查询用户
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param postIds 岗位ids
 | 
				
			||||||
 | 
					     * @return 用户
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    List<UserDTO> selectUsersByPostIds(List<Long> postIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据用户 ID 列表查询用户名称映射关系
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param userIds 用户 ID 列表
 | 
				
			||||||
 | 
					     * @return Map,其中 key 为用户 ID,value 为对应的用户名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Map<Long, String> selectUserNamesByIds(List<Long> userIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据角色 ID 列表查询角色名称映射关系
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param roleIds 角色 ID 列表
 | 
				
			||||||
 | 
					     * @return Map,其中 key 为角色 ID,value 为对应的角色名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据部门 ID 列表查询部门名称映射关系
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param deptIds 部门 ID 列表
 | 
				
			||||||
 | 
					     * @return Map,其中 key 为部门 ID,value 为对应的部门名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据岗位 ID 列表查询岗位名称映射关系
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param postIds 岗位 ID 列表
 | 
				
			||||||
 | 
					     * @return Map,其中 key 为岗位 ID,value 为对应的岗位名称
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Map<Long, String> selectPostNamesByIds(List<Long> postIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.CompleteTaskDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.StartProcessDTO;
 | 
				
			||||||
 | 
					import org.dromara.common.core.domain.dto.StartProcessReturnDTO;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 通用 工作流服务
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author may
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public interface WorkflowService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param businessIds 业务id
 | 
				
			||||||
 | 
					     * @return 结果
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean deleteInstance(List<Long> businessIds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前流程状态
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskId 任务id
 | 
				
			||||||
 | 
					     * @return 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String getBusinessStatusByTaskId(Long taskId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前流程状态
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param businessId 业务id
 | 
				
			||||||
 | 
					     * @return 状态
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    String getBusinessStatus(String businessId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 设置流程变量
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param instanceId 流程实例id
 | 
				
			||||||
 | 
					     * @param variable   流程变量
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void setVariable(Long instanceId, Map<String, Object> variable);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取流程变量
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param instanceId 流程实例id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Map<String, Object> instanceVariable(Long instanceId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 按照业务id查询流程实例id
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param businessId 业务id
 | 
				
			||||||
 | 
					     * @return 结果
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    Long getInstanceIdByBusinessId(String businessId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 新增租户流程定义
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param tenantId 租户id
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void syncDef(String tenantId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 启动流程
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param startProcess 参数
 | 
				
			||||||
 | 
					     * @return 结果
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 办理任务
 | 
				
			||||||
 | 
					     * 系统后台发起审批 无用户信息 需要忽略权限
 | 
				
			||||||
 | 
					     * completeTask.getVariables().put("ignore", true);
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param completeTask 参数
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean completeTask(CompleteTaskDTO completeTask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 办理任务
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param taskId  任务ID
 | 
				
			||||||
 | 
					     * @param message 办理意见
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    boolean completeTask(Long taskId, String message);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,106 +1,157 @@
 | 
				
			|||||||
package org.dromara.common.core.utils;
 | 
					package org.dromara.common.core.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import lombok.AccessLevel;
 | 
					 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					 | 
				
			||||||
import org.apache.commons.lang3.time.DateFormatUtils;
 | 
					import org.apache.commons.lang3.time.DateFormatUtils;
 | 
				
			||||||
 | 
					import org.dromara.common.core.enums.FormatsType;
 | 
				
			||||||
 | 
					import org.dromara.common.core.exception.ServiceException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.lang.management.ManagementFactory;
 | 
					import java.lang.management.ManagementFactory;
 | 
				
			||||||
import java.text.ParseException;
 | 
					import java.text.ParseException;
 | 
				
			||||||
import java.text.SimpleDateFormat;
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
import java.time.LocalDate;
 | 
					import java.time.*;
 | 
				
			||||||
import java.time.LocalDateTime;
 | 
					 | 
				
			||||||
import java.time.LocalTime;
 | 
					 | 
				
			||||||
import java.time.ZoneId;
 | 
					 | 
				
			||||||
import java.time.ZonedDateTime;
 | 
					 | 
				
			||||||
import java.util.Date;
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import java.util.concurrent.TimeUnit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 时间工具类
 | 
					 * 时间工具类
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author ruoyi
 | 
					 * @author ruoyi
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
					 | 
				
			||||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
					public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static final String YYYY = "yyyy";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static final String YYYY_MM = "yyyy-MM";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static final String YYYY_MM_DD = "yyyy-MM-dd";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static final String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final String[] PARSE_PATTERNS = {
 | 
					    private static final String[] PARSE_PATTERNS = {
 | 
				
			||||||
        "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
 | 
					        "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
 | 
				
			||||||
        "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
 | 
					        "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
 | 
				
			||||||
        "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
 | 
					        "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Deprecated
 | 
				
			||||||
 | 
					    private DateUtils() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取当前Date型日期
 | 
					     * 获取当前日期和时间
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return Date() 当前日期
 | 
					     * @return 当前日期和时间的 Date 对象表示
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Date getNowDate() {
 | 
					    public static Date getNowDate() {
 | 
				
			||||||
        return new Date();
 | 
					        return new Date();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取当前日期, 默认格式为yyyy-MM-dd
 | 
					     * 获取当前日期的字符串表示,格式为YYYY-MM-DD
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return String
 | 
					     * @return 当前日期的字符串表示
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static String getDate() {
 | 
					    public static String getDate() {
 | 
				
			||||||
        return dateTimeNow(YYYY_MM_DD);
 | 
					        return dateTimeNow(FormatsType.YYYY_MM_DD);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前日期的字符串表示,格式为yyyyMMdd
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前日期的字符串表示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String getCurrentDate() {
 | 
				
			||||||
 | 
					        return DateFormatUtils.format(new Date(), FormatsType.YYYYMMDD.getTimeFormat());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前日期的路径格式字符串,格式为"yyyy/MM/dd"
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前日期的路径格式字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String datePath() {
 | 
				
			||||||
 | 
					        Date now = new Date();
 | 
				
			||||||
 | 
					        return DateFormatUtils.format(now, FormatsType.YYYY_MM_DD_SLASH.getTimeFormat());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前时间的字符串表示,格式为YYYY-MM-DD HH:MM:SS
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前时间的字符串表示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String getTime() {
 | 
					    public static String getTime() {
 | 
				
			||||||
        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
 | 
					        return dateTimeNow(FormatsType.YYYY_MM_DD_HH_MM_SS);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前时间的字符串表示,格式为 "HH:MM:SS"
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前时间的字符串表示,格式为 "HH:MM:SS"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String getTimeWithHourMinuteSecond() {
 | 
				
			||||||
 | 
					        return dateTimeNow(FormatsType.HH_MM_SS);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前日期和时间的字符串表示,格式为YYYYMMDDHHMMSS
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前日期和时间的字符串表示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String dateTimeNow() {
 | 
					    public static String dateTimeNow() {
 | 
				
			||||||
        return dateTimeNow(YYYYMMDDHHMMSS);
 | 
					        return dateTimeNow(FormatsType.YYYYMMDDHHMMSS);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String dateTimeNow(final String format) {
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前日期和时间的指定格式的字符串表示
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param format 日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
				
			||||||
 | 
					     * @return 当前日期和时间的字符串表示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String dateTimeNow(final FormatsType format) {
 | 
				
			||||||
        return parseDateToStr(format, new Date());
 | 
					        return parseDateToStr(format, new Date());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String dateTime(final Date date) {
 | 
					    /**
 | 
				
			||||||
        return parseDateToStr(YYYY_MM_DD, date);
 | 
					     * 将指定日期格式化为 YYYY-MM-DD 格式的字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param date 要格式化的日期对象
 | 
				
			||||||
 | 
					     * @return 格式化后的日期字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String formatDate(final Date date) {
 | 
				
			||||||
 | 
					        return parseDateToStr(FormatsType.YYYY_MM_DD, date);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static String parseDateToStr(final String format, final Date date) {
 | 
					    /**
 | 
				
			||||||
        return new SimpleDateFormat(format).format(date);
 | 
					     * 将指定日期格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param date 要格式化的日期对象
 | 
				
			||||||
 | 
					     * @return 格式化后的日期时间字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String formatDateTime(final Date date) {
 | 
				
			||||||
 | 
					        return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static Date dateTime(final String format, final String ts) {
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将指定日期按照指定格式进行格式化
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param format 要使用的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
				
			||||||
 | 
					     * @param date   要格式化的日期对象
 | 
				
			||||||
 | 
					     * @return 格式化后的日期时间字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String parseDateToStr(final FormatsType format, final Date date) {
 | 
				
			||||||
 | 
					        return new SimpleDateFormat(format.getTimeFormat()).format(date);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将指定格式的日期时间字符串转换为 Date 对象
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param format 要解析的日期时间格式,例如"YYYY-MM-DD HH:MM:SS"
 | 
				
			||||||
 | 
					     * @param ts     要解析的日期时间字符串
 | 
				
			||||||
 | 
					     * @return 解析后的 Date 对象
 | 
				
			||||||
 | 
					     * @throws RuntimeException 如果解析过程中发生异常
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static Date parseDateTime(final FormatsType format, final String ts) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            return new SimpleDateFormat(format).parse(ts);
 | 
					            return new SimpleDateFormat(format.getTimeFormat()).parse(ts);
 | 
				
			||||||
        } catch (ParseException e) {
 | 
					        } catch (ParseException e) {
 | 
				
			||||||
            throw new RuntimeException(e);
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 日期路径 即年/月/日 如2018/08/08
 | 
					     * 将对象转换为日期对象
 | 
				
			||||||
     */
 | 
					     *
 | 
				
			||||||
    public static String datePath() {
 | 
					     * @param str 要转换的对象,通常是字符串
 | 
				
			||||||
        Date now = new Date();
 | 
					     * @return 转换后的日期对象,如果转换失败或输入为null,则返回null
 | 
				
			||||||
        return DateFormatUtils.format(now, "yyyy/MM/dd");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 日期路径 即年/月/日 如20180808
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static String dateTime() {
 | 
					 | 
				
			||||||
        Date now = new Date();
 | 
					 | 
				
			||||||
        return DateFormatUtils.format(now, "yyyyMMdd");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 日期型字符串转化为日期 格式
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Date parseDate(Object str) {
 | 
					    public static Date parseDate(Object str) {
 | 
				
			||||||
        if (str == null) {
 | 
					        if (str == null) {
 | 
				
			||||||
@@ -115,6 +166,8 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取服务器启动时间
 | 
					     * 获取服务器启动时间
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 服务器启动时间的 Date 对象表示
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Date getServerStartDate() {
 | 
					    public static Date getServerStartDate() {
 | 
				
			||||||
        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
 | 
					        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
 | 
				
			||||||
@@ -122,35 +175,79 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 计算相差天数
 | 
					     * 计算两个时间之间的时间差,并以指定单位返回(绝对值)
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param start 起始时间
 | 
				
			||||||
 | 
					     * @param end   结束时间
 | 
				
			||||||
 | 
					     * @param unit  所需返回的时间单位(DAYS、HOURS、MINUTES、SECONDS、MILLISECONDS、MICROSECONDS、NANOSECONDS)
 | 
				
			||||||
 | 
					     * @return 时间差的绝对值,以指定单位表示
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static int differentDaysByMillisecond(Date date1, Date date2) {
 | 
					    public static long difference(Date start, Date end, TimeUnit unit) {
 | 
				
			||||||
        return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24)));
 | 
					        // 计算时间差,单位为毫秒,取绝对值避免负数
 | 
				
			||||||
 | 
					        long diffInMillis = Math.abs(end.getTime() - start.getTime());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 根据目标单位转换时间差
 | 
				
			||||||
 | 
					        return switch (unit) {
 | 
				
			||||||
 | 
					            case DAYS -> diffInMillis / TimeUnit.DAYS.toMillis(1);
 | 
				
			||||||
 | 
					            case HOURS -> diffInMillis / TimeUnit.HOURS.toMillis(1);
 | 
				
			||||||
 | 
					            case MINUTES -> diffInMillis / TimeUnit.MINUTES.toMillis(1);
 | 
				
			||||||
 | 
					            case SECONDS -> diffInMillis / TimeUnit.SECONDS.toMillis(1);
 | 
				
			||||||
 | 
					            case MILLISECONDS -> diffInMillis;
 | 
				
			||||||
 | 
					            case MICROSECONDS -> TimeUnit.MILLISECONDS.toMicros(diffInMillis);
 | 
				
			||||||
 | 
					            case NANOSECONDS -> TimeUnit.MILLISECONDS.toNanos(diffInMillis);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 计算两个时间差
 | 
					     * 计算两个日期之间的时间差,并以天、小时和分钟的格式返回
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param endDate 结束日期
 | 
				
			||||||
 | 
					     * @param nowDate 当前日期
 | 
				
			||||||
 | 
					     * @return 表示时间差的字符串,格式为"天 小时 分钟"
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static String getDatePoor(Date endDate, Date nowDate) {
 | 
					    public static String getDatePoor(Date endDate, Date nowDate) {
 | 
				
			||||||
        long nd = 1000 * 24 * 60 * 60;
 | 
					        long diffInMillis = endDate.getTime() - nowDate.getTime();
 | 
				
			||||||
        long nh = 1000 * 60 * 60;
 | 
					        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
				
			||||||
        long nm = 1000 * 60;
 | 
					        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
 | 
				
			||||||
        // long ns = 1000;
 | 
					        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
 | 
				
			||||||
        // 获得两个时间的毫秒时间差异
 | 
					        return String.format("%d天 %d小时 %d分钟", day, hour, min);
 | 
				
			||||||
        long diff = endDate.getTime() - nowDate.getTime();
 | 
					 | 
				
			||||||
        // 计算差多少天
 | 
					 | 
				
			||||||
        long day = diff / nd;
 | 
					 | 
				
			||||||
        // 计算差多少小时
 | 
					 | 
				
			||||||
        long hour = diff % nd / nh;
 | 
					 | 
				
			||||||
        // 计算差多少分钟
 | 
					 | 
				
			||||||
        long min = diff % nd % nh / nm;
 | 
					 | 
				
			||||||
        // 计算差多少秒//输出结果
 | 
					 | 
				
			||||||
        // long sec = diff % nd % nh % nm / ns;
 | 
					 | 
				
			||||||
        return day + "天" + hour + "小时" + min + "分钟";
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 增加 LocalDateTime ==> Date
 | 
					     * 计算两个时间点的差值(天、小时、分钟、秒),当值为0时不显示该单位
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param endDate 结束时间
 | 
				
			||||||
 | 
					     * @param nowDate 当前时间
 | 
				
			||||||
 | 
					     * @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String getTimeDifference(Date endDate, Date nowDate) {
 | 
				
			||||||
 | 
					        long diffInMillis = endDate.getTime() - nowDate.getTime();
 | 
				
			||||||
 | 
					        long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
				
			||||||
 | 
					        long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
 | 
				
			||||||
 | 
					        long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
 | 
				
			||||||
 | 
					        long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
 | 
				
			||||||
 | 
					        // 构建时间差字符串,条件是值不为0才显示
 | 
				
			||||||
 | 
					        StringBuilder result = new StringBuilder();
 | 
				
			||||||
 | 
					        if (day > 0) {
 | 
				
			||||||
 | 
					            result.append(String.format("%d天 ", day));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (hour > 0) {
 | 
				
			||||||
 | 
					            result.append(String.format("%d小时 ", hour));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (min > 0) {
 | 
				
			||||||
 | 
					            result.append(String.format("%d分钟 ", min));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (sec > 0) {
 | 
				
			||||||
 | 
					            result.append(String.format("%d秒", sec));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result.length() > 0 ? result.toString().trim() : "0秒";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将 LocalDateTime 对象转换为 Date 对象
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param temporalAccessor 要转换的 LocalDateTime 对象
 | 
				
			||||||
 | 
					     * @return 转换后的 Date 对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Date toDate(LocalDateTime temporalAccessor) {
 | 
					    public static Date toDate(LocalDateTime temporalAccessor) {
 | 
				
			||||||
        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
 | 
					        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
 | 
				
			||||||
@@ -158,11 +255,46 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 增加 LocalDate ==> Date
 | 
					     * 将 LocalDate 对象转换为 Date 对象
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param temporalAccessor 要转换的 LocalDate 对象
 | 
				
			||||||
 | 
					     * @return 转换后的 Date 对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Date toDate(LocalDate temporalAccessor) {
 | 
					    public static Date toDate(LocalDate temporalAccessor) {
 | 
				
			||||||
        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
 | 
					        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
 | 
				
			||||||
        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
 | 
					        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
 | 
				
			||||||
        return Date.from(zdt.toInstant());
 | 
					        return Date.from(zdt.toInstant());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 校验日期范围
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param startDate 开始日期
 | 
				
			||||||
 | 
					     * @param endDate   结束日期
 | 
				
			||||||
 | 
					     * @param maxValue  最大时间跨度的限制值
 | 
				
			||||||
 | 
					     * @param unit      时间跨度的单位,可选择 "DAYS"、"HOURS" 或 "MINUTES"
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void validateDateRange(Date startDate, Date endDate, int maxValue, TimeUnit unit) {
 | 
				
			||||||
 | 
					        // 校验结束日期不能早于开始日期
 | 
				
			||||||
 | 
					        if (endDate.before(startDate)) {
 | 
				
			||||||
 | 
					            throw new ServiceException("结束日期不能早于开始日期");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 计算时间跨度
 | 
				
			||||||
 | 
					        long diffInMillis = endDate.getTime() - startDate.getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 根据单位转换时间跨度
 | 
				
			||||||
 | 
					        long diff = switch (unit) {
 | 
				
			||||||
 | 
					            case DAYS -> TimeUnit.MILLISECONDS.toDays(diffInMillis);
 | 
				
			||||||
 | 
					            case HOURS -> TimeUnit.MILLISECONDS.toHours(diffInMillis);
 | 
				
			||||||
 | 
					            case MINUTES -> TimeUnit.MILLISECONDS.toMinutes(diffInMillis);
 | 
				
			||||||
 | 
					            default -> throw new IllegalArgumentException("不支持的时间单位");
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 校验时间跨度不超过最大限制
 | 
				
			||||||
 | 
					        if (diff > maxValue) {
 | 
				
			||||||
 | 
					            throw new ServiceException("最大时间跨度为 " + maxValue + " " + unit.toString().toLowerCase());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.lang.PatternPool;
 | 
				
			||||||
 | 
					import cn.hutool.core.net.NetUtil;
 | 
				
			||||||
 | 
					import lombok.AccessLevel;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.regex.RegexUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.net.Inet6Address;
 | 
				
			||||||
 | 
					import java.net.InetAddress;
 | 
				
			||||||
 | 
					import java.net.UnknownHostException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 增强网络相关工具类
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author 秋辞未寒
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@Slf4j
 | 
				
			||||||
 | 
					@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
				
			||||||
 | 
					public class NetUtils extends NetUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 判断是否为IPv6地址
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ip IP地址
 | 
				
			||||||
 | 
					     * @return 是否为IPv6地址
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean isIPv6(String ip) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 判断是否为IPv6地址
 | 
				
			||||||
 | 
					            return InetAddress.getByName(ip) instanceof Inet6Address;
 | 
				
			||||||
 | 
					        } catch (UnknownHostException e) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 判断IPv6地址是否为内网地址
 | 
				
			||||||
 | 
					     * <br><br>
 | 
				
			||||||
 | 
					     * 以下地址将归类为本地地址,如有业务场景有需要,请根据需求自行处理:
 | 
				
			||||||
 | 
					     * <pre>
 | 
				
			||||||
 | 
					     * 通配符地址 0:0:0:0:0:0:0:0
 | 
				
			||||||
 | 
					     * 链路本地地址 fe80::/10
 | 
				
			||||||
 | 
					     * 唯一本地地址 fec0::/10
 | 
				
			||||||
 | 
					     * 环回地址 ::1
 | 
				
			||||||
 | 
					     * </pre>
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ip IP地址
 | 
				
			||||||
 | 
					     * @return 是否为内网地址
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean isInnerIPv6(String ip) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 判断是否为IPv6地址
 | 
				
			||||||
 | 
					            if (InetAddress.getByName(ip) instanceof Inet6Address inet6Address) {
 | 
				
			||||||
 | 
					                // isAnyLocalAddress 判断是否为通配符地址,通常不会将其视为内网地址,根据业务场景自行处理判断
 | 
				
			||||||
 | 
					                // isLinkLocalAddress 判断是否为链路本地地址,通常不算内网地址,是否划分归属于内网需要根据业务场景自行处理判断
 | 
				
			||||||
 | 
					                // isLoopbackAddress 判断是否为环回地址,与IPv4的 127.0.0.1 同理,用于表示本机
 | 
				
			||||||
 | 
					                // isSiteLocalAddress 判断是否为本地站点地址,IPv6唯一本地地址(Unique Local Addresses,简称ULA)
 | 
				
			||||||
 | 
					                if (inet6Address.isAnyLocalAddress()
 | 
				
			||||||
 | 
					                    || inet6Address.isLinkLocalAddress()
 | 
				
			||||||
 | 
					                    || inet6Address.isLoopbackAddress()
 | 
				
			||||||
 | 
					                    || inet6Address.isSiteLocalAddress()) {
 | 
				
			||||||
 | 
					                    return true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (UnknownHostException e) {
 | 
				
			||||||
 | 
					            // 注意,isInnerIPv6方法和isIPv6方法的适用范围不同,所以此处不能忽略其异常信息。
 | 
				
			||||||
 | 
					            throw new IllegalArgumentException("Invalid IPv6 address!", e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 判断是否为IPv4地址
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ip IP地址
 | 
				
			||||||
 | 
					     * @return 是否为IPv4地址
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean isIPv4(String ip) {
 | 
				
			||||||
 | 
					        return RegexUtils.isMatch(PatternPool.IPV4, ip);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					package org.dromara.common.core.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
 | 
					import lombok.AccessLevel;
 | 
				
			||||||
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.function.Function;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * 对象工具类
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @author 秋辞未寒
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
				
			||||||
 | 
					public class ObjectUtils extends ObjectUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 如果对象不为空,则获取对象中的某个字段 ObjectUtils.notNullGetter(user, User::getName);
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param obj 对象
 | 
				
			||||||
 | 
					     * @param func 获取方法
 | 
				
			||||||
 | 
					     * @return 对象字段
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T, E> E notNullGetter(T obj, Function<T, E> func) {
 | 
				
			||||||
 | 
					        if (isNotNull(obj) && isNotNull(func)) {
 | 
				
			||||||
 | 
					            return func.apply(obj);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 如果对象不为空,则获取对象中的某个字段,否则返回默认值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param obj          对象
 | 
				
			||||||
 | 
					     * @param func         获取方法
 | 
				
			||||||
 | 
					     * @param defaultValue 默认值
 | 
				
			||||||
 | 
					     * @return 对象字段
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T, E> E notNullGetter(T obj, Function<T, E> func, E defaultValue) {
 | 
				
			||||||
 | 
					        if (isNotNull(obj) && isNotNull(func)) {
 | 
				
			||||||
 | 
					            return func.apply(obj);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return defaultValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 如果值不为空,则返回值,否则返回默认值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param obj          对象
 | 
				
			||||||
 | 
					     * @param defaultValue 默认值
 | 
				
			||||||
 | 
					     * @return 对象字段
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T> T notNull(T obj, T defaultValue) {
 | 
				
			||||||
 | 
					        if (isNotNull(obj)) {
 | 
				
			||||||
 | 
					            return obj;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return defaultValue;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -25,7 +25,7 @@ import java.util.HashMap;
 | 
				
			|||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 客户端工具类
 | 
					 * 客户端工具类,提供获取请求参数、响应处理、头部信息等常用操作
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author ruoyi
 | 
					 * @author ruoyi
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -33,52 +33,73 @@ import java.util.Map;
 | 
				
			|||||||
public class ServletUtils extends JakartaServletUtil {
 | 
					public class ServletUtils extends JakartaServletUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取String参数
 | 
					     * 获取指定名称的 String 类型的请求参数
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name 参数名
 | 
				
			||||||
 | 
					     * @return 参数值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static String getParameter(String name) {
 | 
					    public static String getParameter(String name) {
 | 
				
			||||||
        return getRequest().getParameter(name);
 | 
					        return getRequest().getParameter(name);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取String参数
 | 
					     * 获取指定名称的 String 类型的请求参数,若参数不存在,则返回默认值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name         参数名
 | 
				
			||||||
 | 
					     * @param defaultValue 默认值
 | 
				
			||||||
 | 
					     * @return 参数值或默认值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static String getParameter(String name, String defaultValue) {
 | 
					    public static String getParameter(String name, String defaultValue) {
 | 
				
			||||||
        return Convert.toStr(getRequest().getParameter(name), defaultValue);
 | 
					        return Convert.toStr(getRequest().getParameter(name), defaultValue);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取Integer参数
 | 
					     * 获取指定名称的 Integer 类型的请求参数
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name 参数名
 | 
				
			||||||
 | 
					     * @return 参数值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Integer getParameterToInt(String name) {
 | 
					    public static Integer getParameterToInt(String name) {
 | 
				
			||||||
        return Convert.toInt(getRequest().getParameter(name));
 | 
					        return Convert.toInt(getRequest().getParameter(name));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取Integer参数
 | 
					     * 获取指定名称的 Integer 类型的请求参数,若参数不存在,则返回默认值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name         参数名
 | 
				
			||||||
 | 
					     * @param defaultValue 默认值
 | 
				
			||||||
 | 
					     * @return 参数值或默认值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Integer getParameterToInt(String name, Integer defaultValue) {
 | 
					    public static Integer getParameterToInt(String name, Integer defaultValue) {
 | 
				
			||||||
        return Convert.toInt(getRequest().getParameter(name), defaultValue);
 | 
					        return Convert.toInt(getRequest().getParameter(name), defaultValue);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取Boolean参数
 | 
					     * 获取指定名称的 Boolean 类型的请求参数
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name 参数名
 | 
				
			||||||
 | 
					     * @return 参数值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Boolean getParameterToBool(String name) {
 | 
					    public static Boolean getParameterToBool(String name) {
 | 
				
			||||||
        return Convert.toBool(getRequest().getParameter(name));
 | 
					        return Convert.toBool(getRequest().getParameter(name));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取Boolean参数
 | 
					     * 获取指定名称的 Boolean 类型的请求参数,若参数不存在,则返回默认值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param name         参数名
 | 
				
			||||||
 | 
					     * @param defaultValue 默认值
 | 
				
			||||||
 | 
					     * @return 参数值或默认值
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
 | 
					    public static Boolean getParameterToBool(String name, Boolean defaultValue) {
 | 
				
			||||||
        return Convert.toBool(getRequest().getParameter(name), defaultValue);
 | 
					        return Convert.toBool(getRequest().getParameter(name), defaultValue);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获得所有请求参数
 | 
					     * 获取所有请求参数(以 Map 的形式返回)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param request 请求对象{@link ServletRequest}
 | 
					     * @param request 请求对象{@link ServletRequest}
 | 
				
			||||||
     * @return Map
 | 
					     * @return 请求参数的 Map,键为参数名,值为参数值数组
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Map<String, String[]> getParams(ServletRequest request) {
 | 
					    public static Map<String, String[]> getParams(ServletRequest request) {
 | 
				
			||||||
        final Map<String, String[]> map = request.getParameterMap();
 | 
					        final Map<String, String[]> map = request.getParameterMap();
 | 
				
			||||||
@@ -86,10 +107,10 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获得所有请求参数
 | 
					     * 获取所有请求参数(以 Map 的形式返回,值为字符串形式的拼接)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param request 请求对象{@link ServletRequest}
 | 
					     * @param request 请求对象{@link ServletRequest}
 | 
				
			||||||
     * @return Map
 | 
					     * @return 请求参数的 Map,键为参数名,值为拼接后的字符串
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static Map<String, String> getParamMap(ServletRequest request) {
 | 
					    public static Map<String, String> getParamMap(ServletRequest request) {
 | 
				
			||||||
        Map<String, String> params = new HashMap<>();
 | 
					        Map<String, String> params = new HashMap<>();
 | 
				
			||||||
@@ -100,7 +121,9 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取request
 | 
					     * 获取当前 HTTP 请求对象
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前 HTTP 请求对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static HttpServletRequest getRequest() {
 | 
					    public static HttpServletRequest getRequest() {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -111,7 +134,9 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取response
 | 
					     * 获取当前 HTTP 响应对象
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前 HTTP 响应对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static HttpServletResponse getResponse() {
 | 
					    public static HttpServletResponse getResponse() {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -122,12 +147,25 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取session
 | 
					     * 获取当前请求的 HttpSession 对象
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * 如果当前请求已经关联了一个会话(即已经存在有效的 session ID),
 | 
				
			||||||
 | 
					     * 则返回该会话对象;如果没有关联会话,则会创建一个新的会话对象并返回。
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * HttpSession 用于存储会话级别的数据,如用户登录信息、购物车内容等,
 | 
				
			||||||
 | 
					     * 可以在多个请求之间共享会话数据
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 当前请求的 HttpSession 对象
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static HttpSession getSession() {
 | 
					    public static HttpSession getSession() {
 | 
				
			||||||
        return getRequest().getSession();
 | 
					        return getRequest().getSession();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取当前请求的请求属性
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@link ServletRequestAttributes} 请求属性对象
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static ServletRequestAttributes getRequestAttributes() {
 | 
					    public static ServletRequestAttributes getRequestAttributes() {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
 | 
					            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
 | 
				
			||||||
@@ -137,6 +175,13 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取指定请求头的值,如果头部为空则返回空字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param request 请求对象
 | 
				
			||||||
 | 
					     * @param name    头部名称
 | 
				
			||||||
 | 
					     * @return 头部值
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String getHeader(HttpServletRequest request, String name) {
 | 
					    public static String getHeader(HttpServletRequest request, String name) {
 | 
				
			||||||
        String value = request.getHeader(name);
 | 
					        String value = request.getHeader(name);
 | 
				
			||||||
        if (StringUtils.isEmpty(value)) {
 | 
					        if (StringUtils.isEmpty(value)) {
 | 
				
			||||||
@@ -145,6 +190,12 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
        return urlDecode(value);
 | 
					        return urlDecode(value);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取所有请求头的 Map,键为头部名称,值为头部值
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param request 请求对象
 | 
				
			||||||
 | 
					     * @return 请求头的 Map
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static Map<String, String> getHeaders(HttpServletRequest request) {
 | 
					    public static Map<String, String> getHeaders(HttpServletRequest request) {
 | 
				
			||||||
        Map<String, String> map = new LinkedCaseInsensitiveMap<>();
 | 
					        Map<String, String> map = new LinkedCaseInsensitiveMap<>();
 | 
				
			||||||
        Enumeration<String> enumeration = request.getHeaderNames();
 | 
					        Enumeration<String> enumeration = request.getHeaderNames();
 | 
				
			||||||
@@ -159,7 +210,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 将字符串渲染到客户端
 | 
					     * 将字符串渲染到客户端(以 JSON 格式返回)
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param response 渲染对象
 | 
					     * @param response 渲染对象
 | 
				
			||||||
     * @param string   待渲染的字符串
 | 
					     * @param string   待渲染的字符串
 | 
				
			||||||
@@ -176,37 +227,47 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 是否是Ajax异步请求
 | 
					     * 判断当前请求是否为 Ajax 异步请求
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param request
 | 
					     * @param request 请求对象
 | 
				
			||||||
 | 
					     * @return 是否为 Ajax 请求
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static boolean isAjaxRequest(HttpServletRequest request) {
 | 
					    public static boolean isAjaxRequest(HttpServletRequest request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 判断 Accept 头部是否包含 application/json
 | 
				
			||||||
        String accept = request.getHeader("accept");
 | 
					        String accept = request.getHeader("accept");
 | 
				
			||||||
        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
 | 
					        if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 判断 X-Requested-With 头部是否包含 XMLHttpRequest
 | 
				
			||||||
        String xRequestedWith = request.getHeader("X-Requested-With");
 | 
					        String xRequestedWith = request.getHeader("X-Requested-With");
 | 
				
			||||||
        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
 | 
					        if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 判断 URI 后缀是否为 .json 或 .xml
 | 
				
			||||||
        String uri = request.getRequestURI();
 | 
					        String uri = request.getRequestURI();
 | 
				
			||||||
        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
 | 
					        if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
 | 
				
			||||||
            return true;
 | 
					            return true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 判断请求参数 __ajax 是否为 json 或 xml
 | 
				
			||||||
        String ajax = request.getParameter("__ajax");
 | 
					        String ajax = request.getParameter("__ajax");
 | 
				
			||||||
        return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
 | 
					        return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取客户端 IP 地址
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return 客户端 IP 地址
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static String getClientIP() {
 | 
					    public static String getClientIP() {
 | 
				
			||||||
        return getClientIP(getRequest());
 | 
					        return getClientIP(getRequest());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 内容编码
 | 
					     * 对内容进行 URL 编码
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param str 内容
 | 
					     * @param str 内容
 | 
				
			||||||
     * @return 编码后的内容
 | 
					     * @return 编码后的内容
 | 
				
			||||||
@@ -216,7 +277,7 @@ public class ServletUtils extends JakartaServletUtil {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 内容解码
 | 
					     * 对内容进行 URL 解码
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param str 内容
 | 
					     * @param str 内容
 | 
				
			||||||
     * @return 解码后的内容
 | 
					     * @return 解码后的内容
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
package org.dromara.common.core.utils;
 | 
					package org.dromara.common.core.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import cn.hutool.extra.spring.SpringUtil;
 | 
					import cn.hutool.extra.spring.SpringUtil;
 | 
				
			||||||
import org.springframework.aop.framework.AopContext;
 | 
					 | 
				
			||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 | 
					import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 | 
				
			||||||
 | 
					import org.springframework.boot.autoconfigure.thread.Threading;
 | 
				
			||||||
import org.springframework.context.ApplicationContext;
 | 
					import org.springframework.context.ApplicationContext;
 | 
				
			||||||
 | 
					import org.springframework.core.env.Environment;
 | 
				
			||||||
import org.springframework.stereotype.Component;
 | 
					import org.springframework.stereotype.Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -48,7 +49,7 @@ public final class SpringUtils extends SpringUtil {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @SuppressWarnings("unchecked")
 | 
					    @SuppressWarnings("unchecked")
 | 
				
			||||||
    public static <T> T getAopProxy(T invoker) {
 | 
					    public static <T> T getAopProxy(T invoker) {
 | 
				
			||||||
        return (T) AopContext.currentProxy();
 | 
					        return (T) getBean(invoker.getClass());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -59,4 +60,8 @@ public final class SpringUtils extends SpringUtil {
 | 
				
			|||||||
        return getApplicationContext();
 | 
					        return getApplicationContext();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static boolean isVirtual() {
 | 
				
			||||||
 | 
					        return Threading.VIRTUAL.isActive(getBean(Environment.class));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.function.BiFunction;
 | 
					import java.util.function.BiFunction;
 | 
				
			||||||
 | 
					import java.util.function.Consumer;
 | 
				
			||||||
import java.util.function.Function;
 | 
					import java.util.function.Function;
 | 
				
			||||||
import java.util.function.Predicate;
 | 
					import java.util.function.Predicate;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
@@ -34,6 +35,34 @@ public class StreamUtils {
 | 
				
			|||||||
        return collection.stream().filter(function).collect(Collectors.toList());
 | 
					        return collection.stream().filter(function).collect(Collectors.toList());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 找到流中满足条件的第一个元素
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param collection 需要查询的集合
 | 
				
			||||||
 | 
					     * @param function   过滤方法
 | 
				
			||||||
 | 
					     * @return 找到符合条件的第一个元素,没有则返回null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(collection)) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return collection.stream().filter(function).findFirst().orElse(null);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 找到流中任意一个满足条件的元素
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param collection 需要查询的集合
 | 
				
			||||||
 | 
					     * @param function   过滤方法
 | 
				
			||||||
 | 
					     * @return 找到符合条件的任意一个元素,没有则返回null
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(collection)) {
 | 
				
			||||||
 | 
					            return Optional.empty();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return collection.stream().filter(function).findAny();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 将collection拼接
 | 
					     * 将collection拼接
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,9 @@ import cn.hutool.core.collection.CollUtil;
 | 
				
			|||||||
import cn.hutool.core.convert.Convert;
 | 
					import cn.hutool.core.convert.Convert;
 | 
				
			||||||
import cn.hutool.core.lang.Validator;
 | 
					import cn.hutool.core.lang.Validator;
 | 
				
			||||||
import cn.hutool.core.util.StrUtil;
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
import lombok.AccessLevel;
 | 
					 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					 | 
				
			||||||
import org.springframework.util.AntPathMatcher;
 | 
					import org.springframework.util.AntPathMatcher;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.nio.charset.Charset;
 | 
				
			||||||
import java.util.*;
 | 
					import java.util.*;
 | 
				
			||||||
import java.util.function.Function;
 | 
					import java.util.function.Function;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
@@ -17,11 +16,16 @@ import java.util.stream.Collectors;
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * @author Lion Li
 | 
					 * @author Lion Li
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
					 | 
				
			||||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
					public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static final String SEPARATOR = ",";
 | 
					    public static final String SEPARATOR = ",";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String SLASH = "/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Deprecated
 | 
				
			||||||
 | 
					    private StringUtils() {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 获取参数不为空值
 | 
					     * 获取参数不为空值
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -315,7 +319,47 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
 | 
				
			|||||||
            .stream()
 | 
					            .stream()
 | 
				
			||||||
            .filter(Objects::nonNull)
 | 
					            .filter(Objects::nonNull)
 | 
				
			||||||
            .map(mapper)
 | 
					            .map(mapper)
 | 
				
			||||||
 | 
					            .filter(Objects::nonNull)
 | 
				
			||||||
            .collect(Collectors.toList());
 | 
					            .collect(Collectors.toList());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 不区分大小写检查 CharSequence 是否以指定的前缀开头。
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param str     要检查的 CharSequence 可能为 null
 | 
				
			||||||
 | 
					     * @param prefixs 要查找的前缀可能为 null
 | 
				
			||||||
 | 
					     * @return 是否包含
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean startWithAnyIgnoreCase(CharSequence str, CharSequence... prefixs) {
 | 
				
			||||||
 | 
					        // 判断是否是以指定字符串开头
 | 
				
			||||||
 | 
					        for (CharSequence prefix : prefixs) {
 | 
				
			||||||
 | 
					            if (StringUtils.startsWithIgnoreCase(str, prefix)) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字符串从源字符集转换为目标字符集
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param input       原始字符串
 | 
				
			||||||
 | 
					     * @param fromCharset 源字符集
 | 
				
			||||||
 | 
					     * @param toCharset   目标字符集
 | 
				
			||||||
 | 
					     * @return 转换后的字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String convert(String input, Charset fromCharset, Charset toCharset) {
 | 
				
			||||||
 | 
					        if (isBlank(input)) {
 | 
				
			||||||
 | 
					            return input;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // 从源字符集获取字节
 | 
				
			||||||
 | 
					            byte[] bytes = input.getBytes(fromCharset);
 | 
				
			||||||
 | 
					            // 使用目标字符集解码
 | 
				
			||||||
 | 
					            return new String(bytes, toCharset);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            return input;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,18 +14,6 @@ import java.util.concurrent.*;
 | 
				
			|||||||
@Slf4j
 | 
					@Slf4j
 | 
				
			||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
					@NoArgsConstructor(access = AccessLevel.PRIVATE)
 | 
				
			||||||
public class Threads {
 | 
					public class Threads {
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * sleep等待,单位为毫秒
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public static void sleep(long milliseconds) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            Thread.sleep(milliseconds);
 | 
					 | 
				
			||||||
        } catch (InterruptedException e) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 停止线程池
 | 
					     * 停止线程池
 | 
				
			||||||
     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
 | 
					     * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,13 @@ import cn.hutool.core.lang.tree.Tree;
 | 
				
			|||||||
import cn.hutool.core.lang.tree.TreeNodeConfig;
 | 
					import cn.hutool.core.lang.tree.TreeNodeConfig;
 | 
				
			||||||
import cn.hutool.core.lang.tree.TreeUtil;
 | 
					import cn.hutool.core.lang.tree.TreeUtil;
 | 
				
			||||||
import cn.hutool.core.lang.tree.parser.NodeParser;
 | 
					import cn.hutool.core.lang.tree.parser.NodeParser;
 | 
				
			||||||
import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
					 | 
				
			||||||
import lombok.AccessLevel;
 | 
					import lombok.AccessLevel;
 | 
				
			||||||
import lombok.NoArgsConstructor;
 | 
					import lombok.NoArgsConstructor;
 | 
				
			||||||
 | 
					import org.dromara.common.core.utils.reflect.ReflectUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
 | 
					import java.util.stream.Stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * 扩展 hutool TreeUtil 封装系统树构建
 | 
					 * 扩展 hutool TreeUtil 封装系统树构建
 | 
				
			||||||
@@ -24,12 +26,71 @@ public class TreeBuildUtils extends TreeUtil {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
 | 
					    public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 构建树形结构
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <T>        输入节点的类型
 | 
				
			||||||
 | 
					     * @param <K>        节点ID的类型
 | 
				
			||||||
 | 
					     * @param list       节点列表,其中包含了要构建树形结构的所有节点
 | 
				
			||||||
 | 
					     * @param nodeParser 解析器,用于将输入节点转换为树节点
 | 
				
			||||||
 | 
					     * @return 构建好的树形结构列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
 | 
					    public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
 | 
				
			||||||
        if (CollUtil.isEmpty(list)) {
 | 
					        if (CollUtil.isEmpty(list)) {
 | 
				
			||||||
            return null;
 | 
					            return CollUtil.newArrayList();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
 | 
					        K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
 | 
				
			||||||
        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
 | 
					        return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 构建树形结构
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <T>        输入节点的类型
 | 
				
			||||||
 | 
					     * @param <K>        节点ID的类型
 | 
				
			||||||
 | 
					     * @param parentId   顶级节点
 | 
				
			||||||
 | 
					     * @param list       节点列表,其中包含了要构建树形结构的所有节点
 | 
				
			||||||
 | 
					     * @param nodeParser 解析器,用于将输入节点转换为树节点
 | 
				
			||||||
 | 
					     * @return 构建好的树形结构列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <T, K> List<Tree<K>> build(List<T> list, K parentId, NodeParser<T, K> nodeParser) {
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(list)) {
 | 
				
			||||||
 | 
					            return CollUtil.newArrayList();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return TreeUtil.build(list, parentId, DEFAULT_CONFIG, nodeParser);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取节点列表中所有节点的叶子节点
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <K>   节点ID的类型
 | 
				
			||||||
 | 
					     * @param nodes 节点列表
 | 
				
			||||||
 | 
					     * @return 包含所有叶子节点的列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static <K> List<Tree<K>> getLeafNodes(List<Tree<K>> nodes) {
 | 
				
			||||||
 | 
					        if (CollUtil.isEmpty(nodes)) {
 | 
				
			||||||
 | 
					            return CollUtil.newArrayList();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return nodes.stream()
 | 
				
			||||||
 | 
					            .flatMap(TreeBuildUtils::extractLeafNodes)
 | 
				
			||||||
 | 
					            .collect(Collectors.toList());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 获取指定节点下的所有叶子节点
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param <K>  节点ID的类型
 | 
				
			||||||
 | 
					     * @param node 要查找叶子节点的根节点
 | 
				
			||||||
 | 
					     * @return 包含所有叶子节点的列表
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static <K> Stream<Tree<K>> extractLeafNodes(Tree<K> node) {
 | 
				
			||||||
 | 
					        if (!node.hasChild()) {
 | 
				
			||||||
 | 
					            return Stream.of(node);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // 递归调用,获取所有子节点的叶子节点
 | 
				
			||||||
 | 
					            return node.getChildren().stream()
 | 
				
			||||||
 | 
					                .flatMap(TreeBuildUtils::extractLeafNodes);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user