mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2026-08-27 13:57:13 +00:00
Compare commits
936 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8136a0191a | |||
| 6bfdcae06e | |||
| ba70f9bb2d | |||
| b48fc54151 | |||
| 2779c02953 | |||
| 3965e47d8a | |||
| 91f7fab613 | |||
| d259cac6a0 | |||
| cf006a47da | |||
| 14df8e0d9a | |||
| ddff4ae38d | |||
| a2be9bd3dc | |||
| 3904d80329 | |||
| acab0637bf | |||
| ac374b36b2 | |||
| 631b6be33c | |||
| fe637c4479 | |||
| cb301e214b | |||
| 6afb100ba3 | |||
| 5d1aaa62d6 | |||
| b629c82d0f | |||
| c601164ba3 | |||
| 867a5d084d | |||
| 78aab8f8cd | |||
| c919572eb2 | |||
| 12bcc68b45 | |||
| f551cd938c | |||
| e1a8eea3a1 | |||
| 269de03d0b | |||
| 0bc4961694 | |||
| a71541a227 | |||
| 334c85ed61 | |||
| 0dd3b8dc51 | |||
| 0530a9d307 | |||
| 2d7195c61d | |||
| aaede419bc | |||
| 75d8d374bc | |||
| 07b29e06cf | |||
| 3cbbd0698d | |||
| 5312131635 | |||
| 91f505539e | |||
| c9774e78c4 | |||
| 337c2f7170 | |||
| f773818642 | |||
| 9bf8ae5583 | |||
| d190b89681 | |||
| d89e09b94e | |||
| 1452ae9685 | |||
| 28772b8b30 | |||
| ab201f9d9e | |||
| 1f5f969d20 | |||
| a89a124a0e | |||
| 536b6db527 | |||
| f8e3cff83d | |||
| 6b3ed18c33 | |||
| e669fa3210 | |||
| 2d116146ce | |||
| ed9de38c6f | |||
| 28d4c88387 | |||
| c656f3340d | |||
| cd0ee3f016 | |||
| 48ea66cb1a | |||
| ab6409ea28 | |||
| 34c3b81190 | |||
| 91ba3869e7 | |||
| 1bb597b855 | |||
| 348d7fc534 | |||
| 76218091ad | |||
| 95c9e37797 | |||
| aa277b373b | |||
| 79d9f47053 | |||
| f984f08a14 | |||
| 6f94095bb0 | |||
| 2d4685ac5f | |||
| 7f9e4e14f0 | |||
| c5777c01c1 | |||
| 459e9caf14 | |||
| 0940ba6762 | |||
| d8ed23f227 | |||
| 948eba6566 | |||
| 1a14bdf256 | |||
| bbc684b335 | |||
| 2b8f4e1d2c | |||
| d634c2a292 | |||
| 8b97e7bc53 | |||
| 88f871002c | |||
| 874ad7c9b7 | |||
| 89d6f6f247 | |||
| 1324a1cb16 | |||
| b6eacfa5a8 | |||
| 961bca462e | |||
| 496df8494e | |||
| da4dffcfce | |||
| 2f1f9689e0 | |||
| 8110413fdf | |||
| c1f64d3450 | |||
| cb00f4c9c1 | |||
| 79512c69b2 | |||
| a5fb128f11 | |||
| 8a04e3c88f | |||
| dac447b76f | |||
| 35a9e4c8e8 | |||
| 0d87c12d3c | |||
| f20a0c4342 | |||
| 6c8d637bd2 | |||
| 20e9957db2 | |||
| 9baded9326 | |||
| b5902debb6 | |||
| bcd5bb0f86 | |||
| 1a461f7d3d | |||
| e23d99d85b | |||
| f07c20afab | |||
| 420553eaa6 | |||
| 1d8d93eaa3 | |||
| 5f0d09fd45 | |||
| 1c2b7d7017 | |||
| 5fb2890167 | |||
| 1165c8dc06 | |||
| ee09377997 | |||
| 1921b22a57 | |||
| 8718989c52 | |||
| 36069cd0e4 | |||
| 39b19ac361 | |||
| 279488e7ed | |||
| e28e15d943 | |||
| b44b5551e3 | |||
| 0cb4b35f53 | |||
| 9571e71707 | |||
| dfa7d88255 | |||
| 8d29091afa | |||
| 116fa0053d | |||
| 0c08455b32 | |||
| 581203ba15 | |||
| 50fa220471 | |||
| 287effdc6d | |||
| 1d4fcf737a | |||
| e672a3bc6c | |||
| ec703ceeb8 | |||
| 6aa4e83413 | |||
| 65d677ac90 | |||
| aca2b6d498 | |||
| dd5f72cc99 | |||
| b1d3d87360 | |||
| e67fc5ebd4 | |||
| 6a2c74537e | |||
| 041e226059 | |||
| 0418b6c6ff | |||
| c9272acce2 | |||
| 8d51adee10 | |||
| 6d4cc28dcd | |||
| fc35a1469f | |||
| f70a37c050 | |||
| 181f461984 | |||
| 75618347fa | |||
| 5a57e6b835 | |||
| d1d47d2599 | |||
| f35938a068 | |||
| 888c14615d | |||
| fa6c9696f0 | |||
| 37038449ab | |||
| 9bff358afd | |||
| d2a45156a2 | |||
| 9df0a8de1c | |||
| b58085fde1 | |||
| 5ea8d8c950 | |||
| 3318109044 | |||
| aa1f89e253 | |||
| 35c77403d6 | |||
| 603fb7b92d | |||
| 6cf0c79433 | |||
| 3934e119d6 | |||
| 33a6a21fdf | |||
| 7800b1259f | |||
| 3623fc33d9 | |||
| f8612eb52e | |||
| 8d32b0311a | |||
| 60bcd2d6e9 | |||
| 5ccb511064 | |||
| 78baf6497a | |||
| 0719e53f01 | |||
| 5f2c4205a5 | |||
| 2fe4c96706 | |||
| 5c634940c2 | |||
| 6036f8750b | |||
| dbcd8f58eb | |||
| 8905e232e5 | |||
| 4f15158486 | |||
| d2413abd5c | |||
| f7ffadeaff | |||
| f9eec856e7 | |||
| 62562650fe | |||
| df171097c3 | |||
| 1977aabc9a | |||
| 483c4e6d0a | |||
| b30ffa952f | |||
| f616c6931c | |||
| 26e10293f5 | |||
| 60e578f763 | |||
| 5cd4d8ca11 | |||
| 41a6230b6e | |||
| effda4f6e8 | |||
| af4c38e439 | |||
| fafa8cd573 | |||
| 8909b8a7d4 | |||
| 8ae9bde731 | |||
| a703cb2ad1 | |||
| a918b880d6 | |||
| e795e315eb | |||
| 81869cfeb3 | |||
| fc6f61bc95 | |||
| d44e45ad3b | |||
| 00ed9ddd10 | |||
| 341fc144a1 | |||
| b6b1b2de18 | |||
| c19f2b9e4e | |||
| 3a11f18656 | |||
| 5a43212ccc | |||
| f4cfd1c913 | |||
| 26ce8f30c9 | |||
| 2258962770 | |||
| 655e84012c | |||
| f683ef00b8 | |||
| 424b2ea164 | |||
| 7bb4838132 | |||
| 20516758ea | |||
| 2d5f84ebc2 | |||
| 6bc28e41de | |||
| a4fb3fadaf | |||
| cfa67fcd8c | |||
| e5e8d305d2 | |||
| 9d0084409e | |||
| ee02f46dfd | |||
| 25de0b3530 | |||
| aa76859a05 | |||
| 71b70a59fe | |||
| 05c9528549 | |||
| 1feb2a3861 | |||
| 237e78e80c | |||
| ffc3dcaec9 | |||
| a94e474069 | |||
| 40a0e57870 | |||
| c01ed34602 | |||
| 26a99003d2 | |||
| 93c886d3ed | |||
| 9e1027690b | |||
| cc120c06fd | |||
| 3827da078a | |||
| 70d3505b94 | |||
| a39a69cac5 | |||
| 1dbce3ab7c | |||
| 9742b1b596 | |||
| d98d11ae2d | |||
| 6742dcb33e | |||
| 09a51478a5 | |||
| f02601ab2c | |||
| ac56ca0e81 | |||
| 0fcf77e2ed | |||
| 0f0a3a181e | |||
| e24e2c51e4 | |||
| 2b0dd82d3d | |||
| b97f711eb4 | |||
| 0250ca4eb8 | |||
| 23338995d7 | |||
| 84fd02e7d8 | |||
| ae5bec994d | |||
| 8f3a1b589e | |||
| ad6b3d4b3f | |||
| e2801037cf | |||
| 65061f17fe | |||
| d0f4d93615 | |||
| 5d69832423 | |||
| 0c1e39ea14 | |||
| a39bc870d1 | |||
| 7357912681 | |||
| 901992674e | |||
| 7ceb85ffa0 | |||
| 4672d7de4d | |||
| 87ab6e1744 | |||
| ae0a03728b | |||
| 6fc82a59f1 | |||
| 6c33fa48ec | |||
| 343d5d21d8 | |||
| 0ba909c52e | |||
| 808ce9c25a | |||
| 4351fc5239 | |||
| a545f7fc44 | |||
| acfcdf4d9a | |||
| 9683252783 | |||
| 49c00e162b | |||
| 076a0a44fa | |||
| 0d93589d99 | |||
| 54a8189e27 | |||
| 84f17011ad | |||
| f47bd39644 | |||
| 89f9617ccb | |||
| bf10a13088 | |||
| f2e0361fb6 | |||
| 554152635d | |||
| b379574637 | |||
| 6a556cc6ff | |||
| a6950275ad | |||
| 58b1bf5c33 | |||
| c85f693ca6 | |||
| 5f466fd0c4 | |||
| 127eaf936c | |||
| fcd8556076 | |||
| 0512781513 | |||
| 2472359adb | |||
| 29d4bb4e59 | |||
| cce95424ce | |||
| 8d7358e663 | |||
| 240f10ab45 | |||
| 48213bc9c9 | |||
| 3995d9699d | |||
| ecd4e3eaf0 | |||
| 2e3a42c669 | |||
| 82997fc6cd | |||
| 0dce571270 | |||
| 9375578925 | |||
| e19ccf5064 | |||
| 1cea7b72d7 | |||
| 5da9ddf5e3 | |||
| 93ee01c6b9 | |||
| acd30fda3c | |||
| 3f62a76cc8 | |||
| b0b4e573f6 | |||
| de61899eed | |||
| 3a9bdb36f1 | |||
| b815b8e574 | |||
| 45edee4e63 | |||
| 868bc492a2 | |||
| 90fef1bb17 | |||
| d79b48ea99 | |||
| f6993a1491 | |||
| 6b0b7382a6 | |||
| c41add355f | |||
| 74e3d232f5 | |||
| 03fca40c7d | |||
| b2ad257bd8 | |||
| 7e4f0d73f4 | |||
| 2ec802f17f | |||
| e0ce662c28 | |||
| 3d9ed1b92f | |||
| d4e6e70c43 | |||
| 2095a96e67 | |||
| 328b61b252 | |||
| 781463417c | |||
| 446a14b928 | |||
| 6c2518640b | |||
| d8d138092f | |||
| ec31b736c7 | |||
| e7467b2c5c | |||
| 8050e2f1b1 | |||
| 7de4559b4a | |||
| fc9c0d7657 | |||
| ab3037dc4f | |||
| 8281b838b9 | |||
| 2bc7171abd | |||
| 4f99487d24 | |||
| a7cddc8d40 | |||
| f3c4c02d73 | |||
| eb631360f4 | |||
| a62bf04428 | |||
| 7147f81b42 | |||
| c9098563ca | |||
| d4a8c25eab | |||
| 0ddba506bf | |||
| d02bea85cb | |||
| d27c58bfe8 | |||
| 34bb51f5c0 | |||
| 64c37aaec6 | |||
| 3de036adde | |||
| e0df8c15d8 | |||
| fd5d028e95 | |||
| 176793e15b | |||
| 589ec1fdbc | |||
| b421c8d017 | |||
| e2200bac71 | |||
| f8950d1e20 | |||
| 9dfe9f610d | |||
| 17610e8721 | |||
| f29b787767 | |||
| 9775283a24 | |||
| debc73d7d4 | |||
| 64100cf1ff | |||
| d501e82541 | |||
| 3002585e63 | |||
| 60aca2eef3 | |||
| 5baf342478 | |||
| 3f9919fbee | |||
| 682d8b0099 | |||
| bbabffe191 | |||
| 6722f2eeed | |||
| 5a9728c868 | |||
| eea96e87d9 | |||
| e659740cb8 | |||
| 314909a536 | |||
| 8f5d60f543 | |||
| 1ad0d5387b | |||
| 770c3bd03e | |||
| 4b04a4bf09 | |||
| 1598447f6b | |||
| a8a1db4463 | |||
| 4b47053dcf | |||
| 03054fc1e8 | |||
| 9dce540a09 | |||
| 534182deff | |||
| 4577c45110 | |||
| 0796791ec9 | |||
| 84baac0a4f | |||
| 74d257a610 | |||
| cb8fa6ff9a | |||
| c157012807 | |||
| ffa01bdb3a | |||
| 3fa572f0a8 | |||
| f868de1b7b | |||
| fb785dc17f | |||
| f3d475438f | |||
| d7af327248 | |||
| bd88e27c82 | |||
| 83b6addbba | |||
| d51f3b9f4e | |||
| 9256432532 | |||
| 97d3a31aba | |||
| d9cc85187a | |||
| 2ff2d89b2d | |||
| be2e5059fd | |||
| fad91f01ff | |||
| 8f95374cef | |||
| 7471fa7ee0 | |||
| 529f1e5dbb | |||
| eff131a1ed | |||
| 60b0faa3c6 | |||
| b2d694b90b | |||
| fecc564099 | |||
| 297e920179 | |||
| ea9379a52f | |||
| 0b0f2ee8ea | |||
| 6d2f104a43 | |||
| 7e7d857ba5 | |||
| 2e50e30778 | |||
| daf79683b3 | |||
| 5849ddc160 | |||
| d22b2a10df | |||
| 957a4d1fcd | |||
| c88367939c | |||
| a748d0d62c | |||
| 49ef8378fe | |||
| cd531f1d39 | |||
| 92f73a4a72 | |||
| a4e3f7ea5e | |||
| 26b4561a71 | |||
| dbe276a33b | |||
| 4ab4e1685c | |||
| aab87d322c | |||
| 79ee168293 | |||
| 10e4b0618c | |||
| 7c3316e116 | |||
| 8460316632 | |||
| 5d356aa6c4 | |||
| a776d28294 | |||
| a002a4e7a1 | |||
| 79ec850eca | |||
| d1889c42a3 | |||
| a7ea096319 | |||
| 4e3fc7002d | |||
| 1752695751 | |||
| 2b89c3f8d0 | |||
| 6b387b2456 | |||
| ffc971cf92 | |||
| 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 | |||
| 57dd6831d3 | |||
| 8aa60abb1f | |||
| 7a9f51fc7a | |||
| 3761473967 | |||
| 34031cae8d | |||
| 26abb98747 | |||
| c2f67b4a77 | |||
| 4d925a4d62 | |||
| 4e62054bd1 | |||
| 159e30c982 | |||
| fe40d7db32 | |||
| c0eeafb5cd | |||
| 5626b97a19 | |||
| 0f95e9393d | |||
| 5de1ffff90 | |||
| 33698ee448 | |||
| 5e5d478cf2 | |||
| 778096d100 | |||
| bba163f7b4 | |||
| d4f792810e | |||
| d14ee59912 | |||
| be5d69d5a5 | |||
| 97c36674e4 | |||
| 1f1564fad9 | |||
| c79e053bea | |||
| 92e9ed771b | |||
| 800c6c8ff3 | |||
| 865627fdad | |||
| 192537672e | |||
| 384f9528e7 | |||
| 7334d91d6b | |||
| 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 | |||
| 95c01301f6 | |||
| 934bbe8bd7 | |||
| 718a010c0f | |||
| a87071b834 | |||
| 2c598f93ab | |||
| 0937093851 | |||
| 296466fa13 | |||
| b528f0bd14 | |||
| 7c2c82fc0a | |||
| ffe8b16ff3 | |||
| ecf7ebad53 | |||
| 3bf26cd509 | |||
| a671d4a8a8 | |||
| d9713d0f8c | |||
| aeaa33ebd3 | |||
| c64de03d27 | |||
| 2d99304396 | |||
| a22dc9537f | |||
| 6c28f8a0dd | |||
| c100168374 | |||
| 8636d8b3e8 | |||
| 37b2d648b1 | |||
| 27b4992f6e | |||
| 3c8d864b5f | |||
| 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 | |||
| ea50a57602 | |||
| 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 | |||
| 7e14b98676 | |||
| 761586cc3c | |||
| 6731b7947b | |||
| ddc8bd1139 | |||
| 6ae9bbdb31 | |||
| 015b406001 | |||
| 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 |
@@ -25,6 +25,9 @@ target/
|
|||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
|
|
||||||
|
### VS CODE ###
|
||||||
|
.vscode
|
||||||
|
|
||||||
### JRebel ###
|
### JRebel ###
|
||||||
rebel.xml
|
rebel.xml
|
||||||
|
|
||||||
@@ -38,6 +41,7 @@ nbdist/
|
|||||||
######################################################################
|
######################################################################
|
||||||
# Others
|
# Others
|
||||||
*.log
|
*.log
|
||||||
|
*.log.gz
|
||||||
*.xml.versionsBackup
|
*.xml.versionsBackup
|
||||||
*.swp
|
*.swp
|
||||||
|
|
||||||
|
|||||||
@@ -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.2.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.6.2" />
|
||||||
<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>
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="演示机">
|
<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.2.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-server:5.6.2" />
|
||||||
<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>
|
||||||
</deployment>
|
</deployment>
|
||||||
<method v="2" />
|
<method v="2" />
|
||||||
</configuration>
|
</configuration>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<configuration default="false" name="ruoyi-snailjob-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-snailjob-server:5.2.2" />
|
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.6.2" />
|
||||||
<option name="buildOnly" value="true" />
|
<option name="buildOnly" value="true" />
|
||||||
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
|
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
@@ -5,33 +5,41 @@
|
|||||||
## 平台简介
|
## 平台简介
|
||||||
|
|
||||||
[](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://www.jetbrains.com/?from=RuoYi-Vue-Plus)
|
[](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
|
||||||
<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://github.com/imdap/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://plus-doc.dromara.org) 国内加速: [plus-doc.top](https://plus-doc.top)
|
||||||
|
|
||||||
## 赞助商
|
## 赞助商
|
||||||
|
|
||||||
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
|
MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
|
||||||
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
||||||
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
|
数舵科技 软件定制开发APP小程序等 - https://www.shuduokeji.com/ <br>
|
||||||
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
|
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
|
||||||
[如何成为赞助商 加群联系作者详谈](https://plus-doc.dromara.org/#/common/add_group)
|
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong <br>
|
||||||
|
Ruoyi-Plus-Uniapp - https://ruoyi.plus <br>
|
||||||
|
Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/ <br>
|
||||||
|
稳定低价的大模型中转站 - https://aicodelink.top/register?aff=pHeG <br>
|
||||||
|
|
||||||
|
[如何成为赞助商 加群联系作者详谈 每日PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group)
|
||||||
|
|
||||||
# 本框架与RuoYi的功能差异
|
# 本框架与RuoYi的功能差异
|
||||||
|
|
||||||
@@ -45,7 +53,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||||||
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
|
| 权限注解 | 采用 Sa-Token 支持注解 登录校验、角色校验、权限校验、二级认证校验、HttpBasic校验、忽略校验<br/>角色与权限校验支持多种条件 如 `AND` `OR` 或 `权限 OR 角色` 等复杂表达式 | 只支持是否存在匹配 |
|
||||||
| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 |
|
| 三方鉴权 | 采用 JustAuth 第三方登录组件 支持微信、钉钉等数十种三方认证 | 无 |
|
||||||
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
| 关系数据库支持 | 原生支持 MySQL、Oracle、PostgreSQL、SQLServer<br/>可同时使用异构切换(支持其他 mybatis-plus 支持的所有数据库 只需要增加jdbc依赖即可使用 达梦金仓等均有成功案例) | 支持 Mysql、Oracle 不支持同时使用、不支持异构切换 |
|
||||||
| 缓存数据库 | 支持 Redis 5-7 支持大部分新功能特性 如 分布式限流、分布式队列 | Redis 简单 get set 支持 |
|
| 缓存数据库 | 支持 Redis >= 6 支持大部分新功能特性 如 分布式限流、分布式队列 | 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代码逻辑 |
|
||||||
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
|
| ORM框架 | 采用 Mybatis-Plus 基于对象几乎不用写SQL全java操作 功能强大插件众多<br/>例如多租户插件 分页插件 乐观锁插件等等 | 采用 Mybatis 基于XML需要手写SQL |
|
||||||
@@ -72,7 +80,7 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||||||
| 邮件 | 采用 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/>实时监控服务状态 框架还为其扩展了在线日志查看监控 | 无 |
|
||||||
@@ -110,7 +118,6 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||||||
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
|
| 系统接口 | 根据业务代码自动生成相关的api接口文档 | 支持 | 支持 |
|
||||||
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
|
| 服务监控 | 监视集群系统CPU、内存、磁盘、堆栈、在线日志、Spring相关配置等 | 支持 | 仅支持单机CPU、内存、磁盘监控 |
|
||||||
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
|
| 缓存监控 | 对系统的缓存信息查询,命令统计等。 | 支持 | 支持 |
|
||||||
| 在线构建器 | 拖动表单元素生成相应的HTML代码。 | 支持 | 支持 |
|
|
||||||
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
|
| 使用案例 | 系统的一些功能案例 | 支持 | 不支持 |
|
||||||
|
|
||||||
## 参考文档
|
## 参考文档
|
||||||
@@ -165,8 +172,8 @@ CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
|
|||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|  |  |
|
|  |  |
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,56 +10,54 @@
|
|||||||
|
|
||||||
<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.2.2</revision>
|
<revision>5.6.2</revision>
|
||||||
<spring-boot.version>3.2.9</spring-boot.version>
|
<spring-boot.version>3.5.15</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>
|
||||||
<mybatis.version>3.5.16</mybatis.version>
|
<mybatis.version>3.5.19</mybatis.version>
|
||||||
<springdoc.version>2.6.0</springdoc.version>
|
<springdoc.version>2.8.17</springdoc.version>
|
||||||
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
|
||||||
<easyexcel.version>4.0.2</easyexcel.version>
|
<fastexcel.version>1.3.0</fastexcel.version>
|
||||||
<velocity.version>2.3</velocity.version>
|
<velocity.version>2.3</velocity.version>
|
||||||
<satoken.version>1.38.0</satoken.version>
|
<satoken.version>1.45.0</satoken.version>
|
||||||
<mybatis-plus.version>3.5.7</mybatis-plus.version>
|
<mybatis-plus.version>3.5.16</mybatis-plus.version>
|
||||||
<p6spy.version>3.9.1</p6spy.version>
|
<p6spy.version>3.9.1</p6spy.version>
|
||||||
<hutool.version>5.8.31</hutool.version>
|
<hutool.version>5.8.43</hutool.version>
|
||||||
<okhttp.version>4.10.0</okhttp.version>
|
<spring-boot-admin.version>3.5.8</spring-boot-admin.version>
|
||||||
<spring-boot-admin.version>3.2.3</spring-boot-admin.version>
|
<redisson.version>3.52.0</redisson.version>
|
||||||
<redisson.version>3.34.1</redisson.version>
|
|
||||||
<lock4j.version>2.2.7</lock4j.version>
|
<lock4j.version>2.2.7</lock4j.version>
|
||||||
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
<dynamic-ds.version>4.3.1</dynamic-ds.version>
|
||||||
<snailjob.version>1.1.2</snailjob.version>
|
<snailjob.version>1.10.0</snailjob.version>
|
||||||
<mapstruct-plus.version>1.4.4</mapstruct-plus.version>
|
<mapstruct-plus.version>1.5.0</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.34</lombok.version>
|
<lombok.version>1.18.44</lombok.version>
|
||||||
<bouncycastle.version>1.76</bouncycastle.version>
|
<bouncycastle.version>1.83</bouncycastle.version>
|
||||||
<justauth.version>1.16.6</justauth.version>
|
<justauth.version>1.16.7</justauth.version>
|
||||||
<!-- 离线IP地址定位库 -->
|
<!-- 离线IP地址定位库 -->
|
||||||
<ip2region.version>2.7.0</ip2region.version>
|
<ip2region.version>3.3.7</ip2region.version>
|
||||||
<undertow.version>2.3.15.Final</undertow.version>
|
|
||||||
|
|
||||||
<!-- OSS 配置 -->
|
<!-- OSS 配置 -->
|
||||||
<aws.sdk.version>2.25.15</aws.sdk.version>
|
<aws.sdk.version>2.28.22</aws.sdk.version>
|
||||||
<aws.crt.version>0.29.13</aws.crt.version>
|
|
||||||
<!-- SMS 配置 -->
|
<!-- SMS 配置 -->
|
||||||
<sms4j.version>3.3.2</sms4j.version>
|
<sms4j.version>3.3.5</sms4j.version>
|
||||||
<!-- 限制框架中的fastjson版本 -->
|
<!-- 限制框架中的fastjson版本 -->
|
||||||
<fastjson.version>1.2.83</fastjson.version>
|
<fastjson.version>1.2.83</fastjson.version>
|
||||||
<!-- 面向运行时的D-ORM依赖 -->
|
<!-- 面向运行时的D-ORM依赖 -->
|
||||||
<anyline.version>8.7.2-20240808</anyline.version>
|
<anyline.version>8.7.3-20260319</anyline.version>
|
||||||
<!--工作流配置-->
|
<!-- 工作流配置 -->
|
||||||
<flowable.version>7.0.1</flowable.version>
|
<warm-flow.version>1.8.5</warm-flow.version>
|
||||||
|
|
||||||
<!-- 插件版本 -->
|
<!-- 插件版本 -->
|
||||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
|
||||||
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
|
<maven-war-plugin.version>3.4.0</maven-war-plugin.version>
|
||||||
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
|
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
|
||||||
<maven-surefire-plugin.version>3.1.2</maven-surefire-plugin.version>
|
<maven-surefire-plugin.version>3.5.3</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>
|
||||||
@@ -69,6 +67,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>
|
||||||
@@ -77,6 +77,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>
|
||||||
<!-- 默认环境 -->
|
<!-- 默认环境 -->
|
||||||
@@ -88,6 +90,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>
|
||||||
@@ -114,21 +118,6 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.flowable</groupId>
|
|
||||||
<artifactId>flowable-bom</artifactId>
|
|
||||||
<version>${flowable.version}</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- JustAuth 的依赖配置-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>me.zhyd.oauth</groupId>
|
|
||||||
<artifactId>JustAuth</artifactId>
|
|
||||||
<version>${justauth.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- common 的依赖配置-->
|
<!-- common 的依赖配置-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara</groupId>
|
<groupId>org.dromara</groupId>
|
||||||
@@ -157,9 +146,9 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>cn.idev.excel</groupId>
|
||||||
<artifactId>easyexcel</artifactId>
|
<artifactId>fastexcel</artifactId>
|
||||||
<version>${easyexcel.version}</version>
|
<version>${fastexcel.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- velocity代码生成使用模板 -->
|
<!-- velocity代码生成使用模板 -->
|
||||||
@@ -212,6 +201,12 @@
|
|||||||
<version>${mybatis-plus.version}</version>
|
<version>${mybatis-plus.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.baomidou</groupId>
|
||||||
|
<artifactId>mybatis-plus-jsqlparser</artifactId>
|
||||||
|
<version>${mybatis-plus.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>mybatis-plus-annotation</artifactId>
|
<artifactId>mybatis-plus-annotation</artifactId>
|
||||||
@@ -225,30 +220,24 @@
|
|||||||
<version>${p6spy.version}</version>
|
<version>${p6spy.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.squareup.okhttp3</groupId>
|
|
||||||
<artifactId>okhttp</artifactId>
|
|
||||||
<version>${okhttp.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- AWS SDK for Java 2.x -->
|
<!-- AWS SDK for Java 2.x -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>software.amazon.awssdk</groupId>
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
<artifactId>s3</artifactId>
|
<artifactId>s3</artifactId>
|
||||||
<version>${aws.sdk.version}</version>
|
<version>${aws.sdk.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- 使用AWS基于 CRT 的 S3 客户端 -->
|
<!-- 客户端的性能增强传输管理器 -->
|
||||||
<dependency>
|
|
||||||
<groupId>software.amazon.awssdk.crt</groupId>
|
|
||||||
<artifactId>aws-crt</artifactId>
|
|
||||||
<version>${aws.crt.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>software.amazon.awssdk</groupId>
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
<artifactId>s3-transfer-manager</artifactId>
|
<artifactId>s3-transfer-manager</artifactId>
|
||||||
<version>${aws.sdk.version}</version>
|
<version>${aws.sdk.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- 适用于 Netty 的客户端 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.awssdk</groupId>
|
||||||
|
<artifactId>netty-nio-client</artifactId>
|
||||||
|
<version>${aws.sdk.version}</version>
|
||||||
|
</dependency>
|
||||||
<!--短信sms4j-->
|
<!--短信sms4j-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.dromara.sms4j</groupId>
|
<groupId>org.dromara.sms4j</groupId>
|
||||||
@@ -295,7 +284,7 @@
|
|||||||
<!-- 加密包引入 -->
|
<!-- 加密包引入 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15to18</artifactId>
|
<artifactId>bcpkix-jdk18on</artifactId>
|
||||||
<version>${bouncycastle.version}</version>
|
<version>${bouncycastle.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -305,6 +294,25 @@
|
|||||||
<version>${mapstruct-plus.version}</version>
|
<version>${mapstruct-plus.version}</version>
|
||||||
</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 的依赖配置-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>me.zhyd.oauth</groupId>
|
||||||
|
<artifactId>JustAuth</artifactId>
|
||||||
|
<version>${justauth.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- 离线IP地址定位库 ip2region -->
|
<!-- 离线IP地址定位库 ip2region -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.lionsoul</groupId>
|
<groupId>org.lionsoul</groupId>
|
||||||
@@ -312,28 +320,6 @@
|
|||||||
<version>${ip2region.version}</version>
|
<version>${ip2region.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-core</artifactId>
|
|
||||||
<version>${undertow.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-servlet</artifactId>
|
|
||||||
<version>${undertow.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.undertow</groupId>
|
|
||||||
<artifactId>undertow-websockets-jsr</artifactId>
|
|
||||||
<version>${undertow.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<artifactId>commons-compress</artifactId>
|
|
||||||
<groupId>org.apache.commons</groupId>
|
|
||||||
<version>1.26.2</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>fastjson</artifactId>
|
<artifactId>fastjson</artifactId>
|
||||||
@@ -387,10 +373,9 @@
|
|||||||
<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>
|
<release>${java.version}</release>
|
||||||
<target>${java.version}</target>
|
|
||||||
<encoding>${project.build.sourceEncoding}</encoding>
|
<encoding>${project.build.sourceEncoding}</encoding>
|
||||||
<annotationProcessorPaths>
|
<annotationProcessorPaths>
|
||||||
<path>
|
<path>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
|
||||||
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
|
FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds
|
||||||
#FROM bellsoft/liberica-openjdk-debian:21.0.3-cds
|
#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds
|
||||||
#FROM findepi/graalvm:java17-native
|
#FROM findepi/graalvm:java17-native
|
||||||
|
|
||||||
LABEL maintainer="Lion Li"
|
LABEL maintainer="Lion Li"
|
||||||
@@ -11,13 +11,18 @@ 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 JAVA_OPTS=""
|
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
|
||||||
|
|
||||||
|
SHELL ["/bin/bash", "-c"]
|
||||||
|
|
||||||
ENTRYPOINT java -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 \
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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.exception.NotLoginException;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.core.codec.Base64;
|
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;
|
||||||
@@ -12,7 +13,7 @@ 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;
|
||||||
@@ -20,6 +21,8 @@ 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.encrypt.annotation.ApiEncrypt;
|
||||||
import org.dromara.common.json.utils.JsonUtils;
|
import org.dromara.common.json.utils.JsonUtils;
|
||||||
|
import org.dromara.common.ratelimiter.annotation.RateLimiter;
|
||||||
|
import org.dromara.common.ratelimiter.enums.LimitType;
|
||||||
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;
|
||||||
@@ -45,6 +48,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -92,7 +96,7 @@ public class AuthController {
|
|||||||
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"));
|
||||||
}
|
}
|
||||||
// 校验租户
|
// 校验租户
|
||||||
@@ -103,7 +107,7 @@ public class AuthController {
|
|||||||
Long userId = LoginHelper.getUserId();
|
Long userId = LoginHelper.getUserId();
|
||||||
scheduledExecutorService.schedule(() -> {
|
scheduledExecutorService.schedule(() -> {
|
||||||
SseMessageDto dto = new SseMessageDto();
|
SseMessageDto dto = new SseMessageDto();
|
||||||
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
|
dto.setMessage(DateUtils.getTodayHour(new Date()) + "好,欢迎登录 RuoYi-Vue-Plus 后台管理系统");
|
||||||
dto.setUserIds(List.of(userId));
|
dto.setUserIds(List.of(userId));
|
||||||
SseMessageUtils.publishMessage(dto);
|
SseMessageUtils.publishMessage(dto);
|
||||||
}, 5, TimeUnit.SECONDS);
|
}, 5, TimeUnit.SECONDS);
|
||||||
@@ -111,7 +115,7 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第三方登录请求
|
* 获取跳转URL
|
||||||
*
|
*
|
||||||
* @param source 登录来源
|
* @param source 登录来源
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@@ -133,17 +137,19 @@ public class AuthController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 第三方登录回调业务处理 绑定授权
|
* 前端回调绑定授权(需要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(),
|
||||||
loginBody.getSocialState(), socialProperties);
|
loginBody.getSocialState(), socialProperties);
|
||||||
AuthUser authUserData = response.getData();
|
AuthUser authUserData = response.getData();
|
||||||
// 判断授权响应是否成功
|
// 判断授权响应是否成功
|
||||||
if (!response.ok()) {
|
if (!response.ok()) {
|
||||||
@@ -155,12 +161,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("取消授权失败");
|
||||||
}
|
}
|
||||||
@@ -193,6 +201,7 @@ public class AuthController {
|
|||||||
*
|
*
|
||||||
* @return 租户列表
|
* @return 租户列表
|
||||||
*/
|
*/
|
||||||
|
@RateLimiter(time = 60, count = 20, limitType = LimitType.IP)
|
||||||
@GetMapping("/tenant/list")
|
@GetMapping("/tenant/list")
|
||||||
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
|
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
|
||||||
// 返回对象
|
// 返回对象
|
||||||
@@ -226,7 +235,7 @@ 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);
|
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +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.hutool.captcha.AbstractCaptcha;
|
|
||||||
import cn.hutool.captcha.generator.CodeGenerator;
|
import cn.hutool.captcha.generator.CodeGenerator;
|
||||||
|
import cn.hutool.captcha.generator.MathGenerator;
|
||||||
|
import cn.hutool.captcha.generator.RandomGenerator;
|
||||||
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 jakarta.validation.constraints.NotBlank;
|
||||||
@@ -11,16 +12,16 @@ 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.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.annotation.RateLimiter;
|
||||||
import org.dromara.common.ratelimiter.enums.LimitType;
|
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.core.WaveAndCircleCaptcha;
|
||||||
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.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;
|
||||||
@@ -32,6 +33,7 @@ import org.springframework.validation.annotation.Validated;
|
|||||||
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;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
|
||||||
@@ -79,12 +81,21 @@ public class CaptchaController {
|
|||||||
*
|
*
|
||||||
* @param email 邮箱
|
* @param email 邮箱
|
||||||
*/
|
*/
|
||||||
@RateLimiter(key = "#email", time = 60, count = 1)
|
|
||||||
@GetMapping("/resource/email/code")
|
@GetMapping("/resource/email/code")
|
||||||
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
|
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
|
||||||
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));
|
||||||
@@ -92,45 +103,58 @@ 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成验证码
|
* 生成验证码
|
||||||
*/
|
*/
|
||||||
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
|
|
||||||
@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;
|
||||||
// 生成验证码
|
// 生成验证码
|
||||||
CaptchaType captchaType = captchaProperties.getType();
|
String captchaType = captchaProperties.getType();
|
||||||
boolean isMath = CaptchaType.MATH == captchaType;
|
CodeGenerator codeGenerator;
|
||||||
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
|
if ("math".equals(captchaType)) {
|
||||||
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
|
codeGenerator = new MathGenerator(captchaProperties.getNumberLength(), false);
|
||||||
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
|
} else {
|
||||||
|
codeGenerator = new RandomGenerator(captchaProperties.getCharLength());
|
||||||
|
}
|
||||||
|
WaveAndCircleCaptcha captcha = new WaveAndCircleCaptcha(160, 60);
|
||||||
|
// captcha.setBackground(Color.WHITE); // 不设置就是透明底
|
||||||
|
captcha.setFont(new Font("Arial", Font.BOLD, 45));
|
||||||
captcha.setGenerator(codeGenerator);
|
captcha.setGenerator(codeGenerator);
|
||||||
captcha.createCode();
|
captcha.createCode();
|
||||||
// 如果是数学验证码,使用SpEL表达式处理验证码结果
|
// 如果是数学验证码,使用SpEL表达式处理验证码结果
|
||||||
String code = captcha.getCode();
|
String code = captcha.getCode();
|
||||||
if (isMath) {
|
if ("math".equals(captchaType)) {
|
||||||
ExpressionParser parser = new SpelExpressionParser();
|
ExpressionParser parser = new SpelExpressionParser();
|
||||||
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
|
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package org.dromara.web.listener;
|
package org.dromara.web.listener;
|
||||||
|
|
||||||
import cn.dev33.satoken.config.SaTokenConfig;
|
|
||||||
import cn.dev33.satoken.listener.SaTokenListener;
|
import cn.dev33.satoken.listener.SaTokenListener;
|
||||||
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.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.http.useragent.UserAgent;
|
import cn.hutool.http.useragent.UserAgent;
|
||||||
import cn.hutool.http.useragent.UserAgentUtil;
|
import cn.hutool.http.useragent.UserAgentUtil;
|
||||||
@@ -15,6 +14,7 @@ import org.dromara.common.core.domain.dto.UserOnlineDTO;
|
|||||||
import org.dromara.common.core.utils.MessageUtils;
|
import org.dromara.common.core.utils.MessageUtils;
|
||||||
import org.dromara.common.core.utils.ServletUtils;
|
import org.dromara.common.core.utils.ServletUtils;
|
||||||
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.ip.AddressUtils;
|
import org.dromara.common.core.utils.ip.AddressUtils;
|
||||||
import org.dromara.common.log.event.LogininforEvent;
|
import org.dromara.common.log.event.LogininforEvent;
|
||||||
import org.dromara.common.redis.utils.RedisUtils;
|
import org.dromara.common.redis.utils.RedisUtils;
|
||||||
@@ -35,14 +35,13 @@ import java.time.Duration;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class UserActionListener implements SaTokenListener {
|
public class UserActionListener implements SaTokenListener {
|
||||||
|
|
||||||
private final SaTokenConfig tokenConfig;
|
|
||||||
private final SysLoginService loginService;
|
private final SysLoginService loginService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 每次登录时触发
|
* 每次登录时触发
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
|
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
|
||||||
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||||
String ip = ServletUtils.getClientIP();
|
String ip = ServletUtils.getClientIP();
|
||||||
UserOnlineDTO dto = new UserOnlineDTO();
|
UserOnlineDTO dto = new UserOnlineDTO();
|
||||||
@@ -52,17 +51,17 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
dto.setOs(userAgent.getOs().getName());
|
dto.setOs(userAgent.getOs().getName());
|
||||||
dto.setLoginTime(System.currentTimeMillis());
|
dto.setLoginTime(System.currentTimeMillis());
|
||||||
dto.setTokenId(tokenValue);
|
dto.setTokenId(tokenValue);
|
||||||
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
|
String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY);
|
||||||
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
|
String tenantId = (String) loginParameter.getExtra(LoginHelper.TENANT_KEY);
|
||||||
dto.setUserName(username);
|
dto.setUserName(username);
|
||||||
dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
|
dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY));
|
||||||
dto.setDeviceType(loginModel.getDevice());
|
dto.setDeviceType(loginParameter.getDeviceType());
|
||||||
dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
|
dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY));
|
||||||
TenantHelper.dynamic(tenantId, () -> {
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
if(tokenConfig.getTimeout() == -1) {
|
if(loginParameter.getTimeout() == -1) {
|
||||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
|
||||||
} else {
|
} else {
|
||||||
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
|
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 记录登录日志
|
// 记录登录日志
|
||||||
@@ -74,8 +73,8 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
logininforEvent.setRequest(ServletUtils.getRequest());
|
logininforEvent.setRequest(ServletUtils.getRequest());
|
||||||
SpringUtils.context().publishEvent(logininforEvent);
|
SpringUtils.context().publishEvent(logininforEvent);
|
||||||
// 更新登录信息
|
// 更新登录信息
|
||||||
loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
|
loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip);
|
||||||
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
|
log.info("user doLogin, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,7 +86,7 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
TenantHelper.dynamic(tenantId, () -> {
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
});
|
});
|
||||||
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
|
log.info("user doLogout, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +98,7 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
TenantHelper.dynamic(tenantId, () -> {
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
});
|
});
|
||||||
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
|
log.info("user doKickout, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +110,7 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
TenantHelper.dynamic(tenantId, () -> {
|
TenantHelper.dynamic(tenantId, () -> {
|
||||||
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
|
||||||
});
|
});
|
||||||
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
|
log.info("user doReplaced, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,6 +159,6 @@ public class UserActionListener implements SaTokenListener {
|
|||||||
* 每次Token续期时触发
|
* 每次Token续期时触发
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
|
public void doRenewTimeout(String loginType, Object loginId, String tokenValue, long timeout) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,11 +12,12 @@ 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.CacheConstants;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
|
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.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.*;
|
||||||
@@ -60,6 +61,7 @@ public class SysLoginService {
|
|||||||
private final ISysSocialService sysSocialService;
|
private final ISysSocialService sysSocialService;
|
||||||
private final ISysRoleService roleService;
|
private final ISysRoleService roleService;
|
||||||
private final ISysDeptService deptService;
|
private final ISysDeptService deptService;
|
||||||
|
private final ISysPostService postService;
|
||||||
private final SysUserMapper userMapper;
|
private final SysUserMapper userMapper;
|
||||||
|
|
||||||
|
|
||||||
@@ -148,21 +150,24 @@ public class SysLoginService {
|
|||||||
*/
|
*/
|
||||||
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));
|
||||||
if (ObjectUtil.isNotNull(user.getDeptId())) {
|
if (ObjectUtil.isNotNull(user.getDeptId())) {
|
||||||
Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
|
Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
|
||||||
loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
|
loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
|
||||||
loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
|
loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
|
||||||
}
|
}
|
||||||
List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
|
List<SysRoleVo> roles = roleService.selectRolesByUserId(userId);
|
||||||
|
List<SysPostVo> posts = postService.selectPostsByUserId(userId);
|
||||||
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
|
||||||
|
loginUser.setPosts(BeanUtil.copyToList(posts, PostDTO.class));
|
||||||
return loginUser;
|
return loginUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,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,6 +1,6 @@
|
|||||||
package org.dromara.web.service;
|
package org.dromara.web.service;
|
||||||
|
|
||||||
import cn.dev33.satoken.secure.BCrypt;
|
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 org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
@@ -84,11 +84,11 @@ public class SysRegisterService {
|
|||||||
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 (!StringUtils.equalsIgnoreCase(code, 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;
|
||||||
@@ -58,8 +58,8 @@ public class EmailAuthStrategy implements IAuthStrategy {
|
|||||||
});
|
});
|
||||||
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());
|
||||||
@@ -92,7 +92,7 @@ public class EmailAuthStrategy implements IAuthStrategy {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -70,8 +70,8 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||||||
});
|
});
|
||||||
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());
|
||||||
@@ -102,7 +102,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||||||
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
|
||||||
throw new CaptchaExpireException();
|
throw new CaptchaExpireException();
|
||||||
}
|
}
|
||||||
if (!code.equalsIgnoreCase(captcha)) {
|
if (!StringUtils.equalsIgnoreCase(code, captcha)) {
|
||||||
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
|
||||||
throw new CaptchaException();
|
throw new CaptchaException();
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ public class PasswordAuthStrategy implements IAuthStrategy {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -58,8 +58,8 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
|||||||
});
|
});
|
||||||
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());
|
||||||
@@ -92,7 +92,7 @@ public class SmsAuthStrategy implements IAuthStrategy {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,16 @@
|
|||||||
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.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.http.HttpUtil;
|
|
||||||
import cn.hutool.http.Method;
|
|
||||||
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.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.StreamUtils;
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
@@ -68,15 +65,6 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||||||
throw new ServiceException(response.getMsg());
|
throw new ServiceException(response.getMsg());
|
||||||
}
|
}
|
||||||
AuthUser authUserData = response.getData();
|
AuthUser authUserData = response.getData();
|
||||||
if ("GITEE".equals(authUserData.getSource())) {
|
|
||||||
// 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
|
|
||||||
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
|
|
||||||
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
|
||||||
.executeAsync();
|
|
||||||
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
|
|
||||||
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
|
|
||||||
.executeAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
|
||||||
if (CollUtil.isEmpty(list)) {
|
if (CollUtil.isEmpty(list)) {
|
||||||
@@ -99,8 +87,8 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||||||
});
|
});
|
||||||
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());
|
||||||
@@ -121,7 +109,7 @@ public class SocialAuthStrategy implements IAuthStrategy {
|
|||||||
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", "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,24 @@
|
|||||||
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 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.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.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;
|
||||||
@@ -40,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());
|
||||||
@@ -57,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());
|
||||||
@@ -82,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 用户已被停用 业务逻辑自行实现
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,23 +8,25 @@ spring.boot.admin.client:
|
|||||||
metadata:
|
metadata:
|
||||||
username: ${spring.boot.admin.client.username}
|
username: ${spring.boot.admin.client.username}
|
||||||
userpassword: ${spring.boot.admin.client.password}
|
userpassword: ${spring.boot.admin.client.password}
|
||||||
username: ruoyi
|
username: @monitor.username@
|
||||||
password: 123456
|
password: @monitor.password@
|
||||||
|
|
||||||
--- # snail-job 配置
|
--- # snail-job 配置
|
||||||
snail-job:
|
snail-job:
|
||||||
enabled: true
|
enabled: true
|
||||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||||
group: "ruoyi_group"
|
group: "ruoyi_group"
|
||||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
|
||||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 17888
|
port: 17888
|
||||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
|
||||||
namespace: ${spring.profiles.active}
|
namespace: ${spring.profiles.active}
|
||||||
# 随主应用端口飘逸
|
# 随主应用端口漂移
|
||||||
port: 2${server.port}
|
port: 2${server.port}
|
||||||
|
# 客户端ip指定
|
||||||
|
host:
|
||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
@@ -48,14 +50,14 @@ spring:
|
|||||||
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
|
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&nullCatalogMeansCurrent=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
|
||||||
@@ -116,8 +118,8 @@ redisson:
|
|||||||
nettyThreads: 8
|
nettyThreads: 8
|
||||||
# 单节点配置
|
# 单节点配置
|
||||||
singleServerConfig:
|
singleServerConfig:
|
||||||
# 客户端名称
|
# 客户端名称 不能用中文
|
||||||
clientName: ${ruoyi.name}
|
clientName: RuoYi-Vue-Plus
|
||||||
# 最小空闲连接数
|
# 最小空闲连接数
|
||||||
connectionMinimumIdleSize: 8
|
connectionMinimumIdleSize: 8
|
||||||
# 连接池大小
|
# 连接池大小
|
||||||
@@ -198,7 +200,7 @@ justauth:
|
|||||||
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
redirect-uri: ${justauth.address}/social-callback?source=maxkey
|
||||||
topiam:
|
topiam:
|
||||||
# topiam 服务器地址
|
# topiam 服务器地址
|
||||||
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
|
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
|
||||||
client-id: 449c4*********937************759
|
client-id: 449c4*********937************759
|
||||||
client-secret: ac7***********1e0************28d
|
client-secret: ac7***********1e0************28d
|
||||||
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
redirect-uri: ${justauth.address}/social-callback?source=topiam
|
||||||
@@ -259,3 +261,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
|
||||||
|
|||||||
@@ -11,23 +11,25 @@ spring.boot.admin.client:
|
|||||||
metadata:
|
metadata:
|
||||||
username: ${spring.boot.admin.client.username}
|
username: ${spring.boot.admin.client.username}
|
||||||
userpassword: ${spring.boot.admin.client.password}
|
userpassword: ${spring.boot.admin.client.password}
|
||||||
username: ruoyi
|
username: @monitor.username@
|
||||||
password: 123456
|
password: @monitor.password@
|
||||||
|
|
||||||
--- # snail-job 配置
|
--- # snail-job 配置
|
||||||
snail-job:
|
snail-job:
|
||||||
enabled: true
|
enabled: true
|
||||||
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
|
||||||
group: "ruoyi_group"
|
group: "ruoyi_group"
|
||||||
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
|
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config`表
|
||||||
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
|
||||||
server:
|
server:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 17888
|
port: 17888
|
||||||
# 详见 script/sql/snail_job.sql `sj_namespace` 表
|
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
|
||||||
namespace: ${spring.profiles.active}
|
namespace: ${spring.profiles.active}
|
||||||
# 随主应用端口飘逸
|
# 随主应用端口漂移
|
||||||
port: 2${server.port}
|
port: 2${server.port}
|
||||||
|
# 客户端ip指定
|
||||||
|
host:
|
||||||
|
|
||||||
--- # 数据源配置
|
--- # 数据源配置
|
||||||
spring:
|
spring:
|
||||||
@@ -51,14 +53,14 @@ spring:
|
|||||||
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
|
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&nullCatalogMeansCurrent=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
|
||||||
@@ -119,8 +121,8 @@ redisson:
|
|||||||
nettyThreads: 32
|
nettyThreads: 32
|
||||||
# 单节点配置
|
# 单节点配置
|
||||||
singleServerConfig:
|
singleServerConfig:
|
||||||
# 客户端名称
|
# 客户端名称 不能用中文
|
||||||
clientName: ${ruoyi.name}
|
clientName: RuoYi-Vue-Plus
|
||||||
# 最小空闲连接数
|
# 最小空闲连接数
|
||||||
connectionMinimumIdleSize: 32
|
connectionMinimumIdleSize: 32
|
||||||
# 连接池大小
|
# 连接池大小
|
||||||
@@ -261,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
|
||||||
|
|||||||
@@ -1,24 +1,3 @@
|
|||||||
# 项目相关配置
|
|
||||||
ruoyi:
|
|
||||||
# 名称
|
|
||||||
name: RuoYi-Vue-Plus
|
|
||||||
# 版本
|
|
||||||
version: ${revision}
|
|
||||||
# 版权年份
|
|
||||||
copyrightYear: 2024
|
|
||||||
|
|
||||||
captcha:
|
|
||||||
enable: true
|
|
||||||
# 页面 <参数设置> 可开启关闭 验证码校验
|
|
||||||
# 验证码类型 math 数组计算 char 字符验证
|
|
||||||
type: MATH
|
|
||||||
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
|
|
||||||
category: CIRCLE
|
|
||||||
# 数字验证码位数
|
|
||||||
numberLength: 1
|
|
||||||
# 字符验证码长度
|
|
||||||
charLength: 4
|
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
# 服务器的HTTP端口,默认为8080
|
||||||
@@ -29,7 +8,7 @@ server:
|
|||||||
# undertow 配置
|
# undertow 配置
|
||||||
undertow:
|
undertow:
|
||||||
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
||||||
max-http-post-size: -1
|
max-http-post-size: 1GB
|
||||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
||||||
# 每块buffer的空间大小,越小的空间被利用越充分
|
# 每块buffer的空间大小,越小的空间被利用越充分
|
||||||
buffer-size: 512
|
buffer-size: 512
|
||||||
@@ -41,12 +20,23 @@ server:
|
|||||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
||||||
worker: 256
|
worker: 256
|
||||||
|
|
||||||
|
captcha:
|
||||||
|
# 是否启用验证码校验
|
||||||
|
enable: true
|
||||||
|
# 验证码类型 math 数组计算 char 字符验证
|
||||||
|
type: math
|
||||||
|
# 数字验证码位数
|
||||||
|
numberLength: 1
|
||||||
|
# 字符验证码长度
|
||||||
|
charLength: 4
|
||||||
|
|
||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
org.dromara: @logging.level@
|
org.dromara: @logging.level@
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
org.mybatis.spring.mapper: error
|
org.mybatis.spring.mapper: error
|
||||||
|
org.apache.fury: warn
|
||||||
config: classpath:logback-plus.xml
|
config: classpath:logback-plus.xml
|
||||||
|
|
||||||
# 用户配置
|
# 用户配置
|
||||||
@@ -60,11 +50,18 @@ user:
|
|||||||
# Spring配置
|
# Spring配置
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: ${ruoyi.name}
|
name: RuoYi-Vue-Plus
|
||||||
threads:
|
threads:
|
||||||
# 开启虚拟线程 仅jdk21可用
|
# 开启虚拟线程 仅jdk21可用
|
||||||
virtual:
|
virtual:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
task:
|
||||||
|
execution:
|
||||||
|
# 从 springboot 3.5 开始 spring自带线程池
|
||||||
|
# 不再需要 AsyncConfig与ThreadPoolConfig 可直接注入线程池使用
|
||||||
|
thread-name-prefix: async-
|
||||||
|
# 由spring自己初始化线程池
|
||||||
|
mode: force
|
||||||
# 资源信息
|
# 资源信息
|
||||||
messages:
|
messages:
|
||||||
# 国际化资源文件路径
|
# 国际化资源文件路径
|
||||||
@@ -110,17 +107,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/**
|
||||||
|
- /warm-flow-ui/config
|
||||||
|
|
||||||
# 多租户配置
|
# 多租户配置
|
||||||
tenant:
|
tenant:
|
||||||
@@ -137,10 +132,13 @@ tenant:
|
|||||||
- sys_user_role
|
- sys_user_role
|
||||||
- sys_client
|
- sys_client
|
||||||
- sys_oss_config
|
- sys_oss_config
|
||||||
|
- flow_spel
|
||||||
|
|
||||||
# MyBatisPlus配置
|
# MyBatisPlus配置
|
||||||
# https://baomidou.com/config/
|
# https://baomidou.com/config/
|
||||||
mybatis-plus:
|
mybatis-plus:
|
||||||
|
# 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
|
||||||
|
enableLogicDelete: true
|
||||||
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
|
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
|
||||||
mapperPackage: org.dromara.**.mapper
|
mapperPackage: org.dromara.**.mapper
|
||||||
# 对应的 XML 文件位置
|
# 对应的 XML 文件位置
|
||||||
@@ -185,28 +183,18 @@ 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模块...'
|
||||||
# 版本
|
# 版本
|
||||||
version: '版本号: ${ruoyi.version}'
|
version: '版本号: ${project.version}'
|
||||||
# 作者信息
|
# 作者信息
|
||||||
contact:
|
contact:
|
||||||
name: Lion Li
|
name: Lion Li
|
||||||
email: crazylionli@163.com
|
email: crazylionli@163.com
|
||||||
url: https://gitee.com/dromara/RuoYi-Vue-Plus
|
url: https://gitee.com/dromara/RuoYi-Vue-Plus
|
||||||
components:
|
|
||||||
# 鉴权方式配置
|
|
||||||
security-schemes:
|
|
||||||
apiKey:
|
|
||||||
type: APIKEY
|
|
||||||
in: HEADER
|
|
||||||
name: ${sa-token.token-name}
|
|
||||||
#这里定义了两个分组,可定义多个,也可以不定义
|
#这里定义了两个分组,可定义多个,也可以不定义
|
||||||
group-configs:
|
group-configs:
|
||||||
- group: 1.演示模块
|
- group: 1.演示模块
|
||||||
@@ -217,25 +205,17 @@ 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/*
|
- /warm-flow/save-json
|
||||||
|
|
||||||
# 全局线程池相关配置
|
|
||||||
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
|
|
||||||
thread-pool:
|
|
||||||
# 是否开启线程池
|
|
||||||
enabled: false
|
|
||||||
# 队列最大长度
|
|
||||||
queueCapacity: 128
|
|
||||||
# 线程池维护线程所允许的空闲时间
|
|
||||||
keepAliveSeconds: 300
|
|
||||||
|
|
||||||
--- # 分布式锁 lock4j 全局配置
|
--- # 分布式锁 lock4j 全局配置
|
||||||
lock4j:
|
lock4j:
|
||||||
@@ -270,24 +250,15 @@ websocket:
|
|||||||
# 设置访问源地址
|
# 设置访问源地址
|
||||||
allowedOrigins: '*'
|
allowedOrigins: '*'
|
||||||
|
|
||||||
--- #flowable配置
|
--- # warm-flow工作流配置
|
||||||
flowable:
|
warm-flow:
|
||||||
# 开关 用于启动/停用工作流
|
# 是否开启工作流,默认true
|
||||||
enabled: true
|
enabled: true
|
||||||
process.enabled: ${flowable.enabled}
|
# 是否开启设计器ui
|
||||||
eventregistry.enabled: ${flowable.enabled}
|
ui: true
|
||||||
async-executor-activate: false #关闭定时任务JOB
|
# 是否显示流程图顶部文字
|
||||||
# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
|
top-text-show: true
|
||||||
database-schema-update: true
|
# 是否渲染节点悬浮提示,默认true
|
||||||
activity-font-name: 宋体
|
node-tooltip: true
|
||||||
label-font-name: 宋体
|
# 默认Authorization,如果有多个token,用逗号分隔
|
||||||
annotation-font-name: 宋体
|
token-name: ${sa-token.token-name},clientid
|
||||||
# 关闭各个模块生成表,目前只使用工作流基础表
|
|
||||||
idm:
|
|
||||||
enabled: false
|
|
||||||
cmmn:
|
|
||||||
enabled: false
|
|
||||||
dmn:
|
|
||||||
enabled: false
|
|
||||||
app:
|
|
||||||
enabled: false
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
|
|||||||
user.not.exists=对不起, 您的账号:{0} 不存在.
|
user.not.exists=对不起, 您的账号:{0} 不存在.
|
||||||
user.password.not.match=用户不存在/密码错误
|
user.password.not.match=用户不存在/密码错误
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
user.password.retry.limit.count=密码输入错误{0}次
|
||||||
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
|
user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
|
||||||
user.password.delete=对不起,您的账号:{0} 已被删除
|
user.password.delete=对不起,您的账号:{0} 已被删除
|
||||||
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
||||||
role.blocked=角色已封禁,请联系管理员
|
role.blocked=角色已封禁,请联系管理员
|
||||||
@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
|||||||
user.password.not.blank=用户密码不能为空
|
user.password.not.blank=用户密码不能为空
|
||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
|
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
user.email.not.blank=邮箱不能为空
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
@@ -46,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
|
|||||||
rate.limiter.message=访问过于频繁,请稍候再试
|
rate.limiter.message=访问过于频繁,请稍候再试
|
||||||
sms.code.not.blank=短信验证码不能为空
|
sms.code.not.blank=短信验证码不能为空
|
||||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
||||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
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.source.not.blank=第三方登录平台[source]不能为空
|
||||||
social.code.not.blank=第三方登录平台[code]不能为空
|
social.code.not.blank=第三方登录平台[code]不能为空
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ user.username.length.valid=Account length must be between {min} and {max} charac
|
|||||||
user.password.not.blank=Password cannot be empty
|
user.password.not.blank=Password cannot be empty
|
||||||
user.password.length.valid=Password length must be between {min} and {max} characters
|
user.password.length.valid=Password length must be between {min} and {max} characters
|
||||||
user.password.not.valid=* 5-50 characters
|
user.password.not.valid=* 5-50 characters
|
||||||
|
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
|
||||||
user.email.not.valid=Mailbox format error
|
user.email.not.valid=Mailbox format error
|
||||||
user.email.not.blank=Mailbox cannot be blank
|
user.email.not.blank=Mailbox cannot be blank
|
||||||
user.phonenumber.not.blank=Phone number cannot be blank
|
user.phonenumber.not.blank=Phone number cannot be blank
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
|
|||||||
user.not.exists=对不起, 您的账号:{0} 不存在.
|
user.not.exists=对不起, 您的账号:{0} 不存在.
|
||||||
user.password.not.match=用户不存在/密码错误
|
user.password.not.match=用户不存在/密码错误
|
||||||
user.password.retry.limit.count=密码输入错误{0}次
|
user.password.retry.limit.count=密码输入错误{0}次
|
||||||
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
|
user.password.retry.limit.exceed=密码输入错误{0}次,账户锁定{1}分钟
|
||||||
user.password.delete=对不起,您的账号:{0} 已被删除
|
user.password.delete=对不起,您的账号:{0} 已被删除
|
||||||
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
|
||||||
role.blocked=角色已封禁,请联系管理员
|
role.blocked=角色已封禁,请联系管理员
|
||||||
@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
|
|||||||
user.password.not.blank=用户密码不能为空
|
user.password.not.blank=用户密码不能为空
|
||||||
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
|
||||||
user.password.not.valid=* 5-50个字符
|
user.password.not.valid=* 5-50个字符
|
||||||
|
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
|
||||||
user.email.not.valid=邮箱格式错误
|
user.email.not.valid=邮箱格式错误
|
||||||
user.email.not.blank=邮箱不能为空
|
user.email.not.blank=邮箱不能为空
|
||||||
user.phonenumber.not.blank=用户手机号不能为空
|
user.phonenumber.not.blank=用户手机号不能为空
|
||||||
@@ -46,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
|
|||||||
rate.limiter.message=访问过于频繁,请稍候再试
|
rate.limiter.message=访问过于频繁,请稍候再试
|
||||||
sms.code.not.blank=短信验证码不能为空
|
sms.code.not.blank=短信验证码不能为空
|
||||||
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
sms.code.retry.limit.count=短信验证码输入错误{0}次
|
||||||
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
|
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.source.not.blank=第三方登录平台[source]不能为空
|
||||||
social.code.not.blank=第三方登录平台[code]不能为空
|
social.code.not.blank=第三方登录平台[code]不能为空
|
||||||
|
|||||||
BIN
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"/>
|
||||||
|
|
||||||
<!-- 控制台输出 -->
|
<!-- 控制台输出 -->
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<!-- 循环政策:基于时间创建日志文件 -->
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<!-- 日志文件名格式 -->
|
<!-- 日志文件名格式 -->
|
||||||
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||||
<!-- 日志最大的历史 60天 -->
|
<!-- 日志最大的历史 60天 -->
|
||||||
<maxHistory>60</maxHistory>
|
<maxHistory>60</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<!-- 循环政策:基于时间创建日志文件 -->
|
<!-- 循环政策:基于时间创建日志文件 -->
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||||
<!-- 日志文件名格式 -->
|
<!-- 日志文件名格式 -->
|
||||||
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
|
||||||
<!-- 日志最大的历史 60天 -->
|
<!-- 日志最大的历史 60天 -->
|
||||||
<maxHistory>60</maxHistory>
|
<maxHistory>60</maxHistory>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
</description>
|
</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>5.2.2</revision>
|
<revision>5.6.2</revision>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
|||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
package org.dromara.common.core.config;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
|
||||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
|
||||||
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
|
||||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步配置
|
|
||||||
* <p>
|
|
||||||
* 如果未使用虚拟线程则生效
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@AutoConfiguration
|
|
||||||
public class AsyncConfig implements AsyncConfigurer {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义 @Async 注解使用系统线程池
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Executor getAsyncExecutor() {
|
|
||||||
if(SpringUtils.isVirtual()) {
|
|
||||||
return new VirtualThreadTaskExecutor("async-");
|
|
||||||
}
|
|
||||||
return SpringUtils.getBean("scheduledExecutorService");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步执行异常处理
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
|
||||||
return (throwable, method, objects) -> {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("Exception message - ").append(throwable.getMessage())
|
|
||||||
.append(", Method name - ").append(method.getName());
|
|
||||||
if (ArrayUtil.isNotEmpty(objects)) {
|
|
||||||
sb.append(", Parameter value - ").append(Arrays.toString(objects));
|
|
||||||
}
|
|
||||||
throw new ServiceException(sb.toString());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-33
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
+55
-24
@@ -3,17 +3,12 @@ package org.dromara.common.core.config;
|
|||||||
import jakarta.annotation.PreDestroy;
|
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.utils.SpringUtils;
|
||||||
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.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
import org.springframework.core.task.VirtualThreadTaskExecutor;
|
||||||
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 线程池配置
|
* 线程池配置
|
||||||
@@ -22,7 +17,6 @@ import java.util.concurrent.ThreadPoolExecutor;
|
|||||||
**/
|
**/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@EnableConfigurationProperties(ThreadPoolProperties.class)
|
|
||||||
public class ThreadPoolConfig {
|
public class ThreadPoolConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,30 +26,25 @@ public class ThreadPoolConfig {
|
|||||||
|
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
|
||||||
@Bean(name = "threadPoolTaskExecutor")
|
|
||||||
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
|
|
||||||
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
|
|
||||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
||||||
executor.setCorePoolSize(core);
|
|
||||||
executor.setMaxPoolSize(core * 2);
|
|
||||||
executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
|
|
||||||
executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
|
|
||||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行周期性或定时任务
|
* 执行周期性或定时任务
|
||||||
*/
|
*/
|
||||||
@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) {
|
||||||
super.afterExecute(r, t);
|
super.afterExecute(r, t);
|
||||||
Threads.printException(r, t);
|
printException(r, t);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.scheduledExecutorService = scheduledThreadPoolExecutor;
|
this.scheduledExecutorService = scheduledThreadPoolExecutor;
|
||||||
@@ -64,15 +53,57 @@ public class ThreadPoolConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 销毁事件
|
* 销毁事件
|
||||||
|
* 停止线程池
|
||||||
|
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
||||||
|
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
||||||
|
* 如果仍然超時,則強制退出.
|
||||||
|
* 另对在shutdown时线程本身被调用中断做了处理.
|
||||||
*/
|
*/
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
try {
|
try {
|
||||||
log.info("====关闭后台任务任务线程池====");
|
log.info("====关闭后台任务任务线程池====");
|
||||||
Threads.shutdownAndAwaitTermination(scheduledExecutorService);
|
ScheduledExecutorService pool = scheduledExecutorService;
|
||||||
|
if (pool != null && !pool.isShutdown()) {
|
||||||
|
pool.shutdown();
|
||||||
|
try {
|
||||||
|
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
||||||
|
pool.shutdownNow();
|
||||||
|
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
||||||
|
log.info("Pool did not terminate");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
pool.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error(e.getMessage(), e);
|
log.error(e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印线程异常信息
|
||||||
|
*/
|
||||||
|
public static void printException(Runnable r, Throwable t) {
|
||||||
|
if (t == null && r instanceof Future<?>) {
|
||||||
|
try {
|
||||||
|
Future<?> future = (Future<?>) r;
|
||||||
|
if (future.isDone()) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
} catch (CancellationException ce) {
|
||||||
|
t = ce;
|
||||||
|
} catch (ExecutionException ee) {
|
||||||
|
t = ee.getCause();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (t != null) {
|
||||||
|
log.error(t.getMessage(), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -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);
|
||||||
// 加载配置
|
// 加载配置
|
||||||
|
|||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
package org.dromara.common.core.config.properties;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 线程池 配置属性
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@ConfigurationProperties(prefix = "thread-pool")
|
|
||||||
public class ThreadPoolProperties {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否开启线程池
|
|
||||||
*/
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 队列最大长度
|
|
||||||
*/
|
|
||||||
private int queueCapacity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 线程池维护线程所允许的空闲时间
|
|
||||||
*/
|
|
||||||
private int keepAliveSeconds;
|
|
||||||
|
|
||||||
}
|
|
||||||
+19
-3
@@ -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,6 +31,11 @@ public interface CacheNames {
|
|||||||
*/
|
*/
|
||||||
String SYS_DICT = "sys_dict";
|
String SYS_DICT = "sys_dict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据字典类型
|
||||||
|
*/
|
||||||
|
String SYS_DICT_TYPE = "sys_dict_type";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 租户
|
* 租户
|
||||||
*/
|
*/
|
||||||
@@ -46,7 +52,7 @@ public interface CacheNames {
|
|||||||
String SYS_USER_NAME = "sys_user_name#30d";
|
String SYS_USER_NAME = "sys_user_name#30d";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名称
|
* 用户昵称
|
||||||
*/
|
*/
|
||||||
String SYS_NICKNAME = "sys_nickname#30d";
|
String SYS_NICKNAME = "sys_nickname#30d";
|
||||||
|
|
||||||
@@ -60,6 +66,16 @@ 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配置
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
-6
@@ -68,14 +68,14 @@ 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密头
|
||||||
|
*/
|
||||||
|
String ENCRYPT_HEADER = "ENC_";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -17,9 +17,14 @@ public interface RegexConstants extends RegexPool {
|
|||||||
String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
|
String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限标识必须符合 tool:build:list 格式,或者空字符串
|
* 权限标识必须符合以下格式:
|
||||||
|
* 1. 标准格式:xxx:yyy:zzz
|
||||||
|
* - 第一部分(xxx):只能包含字母、数字和下划线(_),不能使用 `*`
|
||||||
|
* - 第二部分(yyy):可以包含字母、数字、下划线(_)和 `*`
|
||||||
|
* - 第三部分(zzz):可以包含字母、数字、下划线(_)和 `*`
|
||||||
|
* 2. 允许空字符串(""),表示没有权限标识
|
||||||
*/
|
*/
|
||||||
String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
|
String PERMISSION_STRING = "^$|^[a-zA-Z0-9_]+:[a-zA-Z0-9_*]+:[a-zA-Z0-9_*]+$";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 身份证号码(后6位)
|
* 身份证号码(后6位)
|
||||||
|
|||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认部门 ID
|
||||||
|
*/
|
||||||
|
Long DEFAULT_DEPT_ID = 100L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排除敏感属性字段
|
||||||
|
*/
|
||||||
|
String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
-10
@@ -7,16 +7,6 @@ package org.dromara.common.core.constant;
|
|||||||
*/
|
*/
|
||||||
public interface TenantConstants {
|
public interface TenantConstants {
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户正常状态
|
|
||||||
*/
|
|
||||||
String NORMAL = "0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 租户封禁状态
|
|
||||||
*/
|
|
||||||
String DISABLE = "1";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 超级管理员ID
|
* 超级管理员ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
-152
@@ -1,152 +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 DEL_FLAG_NORMAL = "0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通用删除标志
|
|
||||||
*/
|
|
||||||
String DEL_FLAG_REMOVED = "2";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否为系统默认(是)
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
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 String handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程变量
|
||||||
|
*/
|
||||||
|
private Map<String, Object> variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩展变量(此处为逗号分隔的ossId)
|
||||||
|
*/
|
||||||
|
private String ext;
|
||||||
|
|
||||||
|
public Map<String, Object> getVariables() {
|
||||||
|
if (variables == null) {
|
||||||
|
variables = new HashMap<>(16);
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+36
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+41
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+41
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+9
-7
@@ -1,28 +1,30 @@
|
|||||||
package org.dromara.workflow.domain.vo;
|
package org.dromara.common.core.domain.dto;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程变量
|
* 抄送
|
||||||
*
|
*
|
||||||
* @author may
|
* @author may
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class VariableVo implements Serializable {
|
public class FlowCopyDTO implements Serializable {
|
||||||
|
|
||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量key
|
* 用户id
|
||||||
*/
|
*/
|
||||||
private String key;
|
private Long userId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 变量值
|
* 用户昵称
|
||||||
*/
|
*/
|
||||||
private String value;
|
private String nickName;
|
||||||
|
|
||||||
}
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package org.dromara.common.core.domain.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程实例业务扩展对象
|
||||||
|
*
|
||||||
|
* @author may
|
||||||
|
* @date 2025-08-05
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class FlowInstanceBizExtDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程实例ID
|
||||||
|
*/
|
||||||
|
private Long instanceId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务ID
|
||||||
|
*/
|
||||||
|
private String businessId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务编码
|
||||||
|
*/
|
||||||
|
private String businessCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务标题
|
||||||
|
*/
|
||||||
|
private String businessTitle;
|
||||||
|
|
||||||
|
}
|
||||||
+46
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -35,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;
|
||||||
|
|
||||||
|
|||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
package org.dromara.common.core.domain.dto;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理人(可不填 用于覆盖当前节点办理人)
|
||||||
|
*/
|
||||||
|
private String handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}}
|
||||||
|
*/
|
||||||
|
private Map<String, Object> variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程业务扩展信息
|
||||||
|
*/
|
||||||
|
private FlowInstanceBizExtDTO bizExt;
|
||||||
|
|
||||||
|
public Map<String, Object> getVariables() {
|
||||||
|
if (variables == null) {
|
||||||
|
variables = new HashMap<>(16);
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue()));
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FlowInstanceBizExtDTO getBizExt() {
|
||||||
|
if (ObjectUtil.isNull(bizExt)) {
|
||||||
|
bizExt = new FlowInstanceBizExtDTO();
|
||||||
|
}
|
||||||
|
return bizExt;
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+101
@@ -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, String> storageId,
|
||||||
|
Function<T, String> handlerCode,
|
||||||
|
Function<T, String> handlerName,
|
||||||
|
Function<T, String> groupName,
|
||||||
|
Function<T, Date> createTimeMapper) {
|
||||||
|
return sourceList.stream()
|
||||||
|
.map(item -> new TaskHandler(
|
||||||
|
storageId.apply(item),
|
||||||
|
handlerCode.apply(item),
|
||||||
|
handlerName.apply(item),
|
||||||
|
groupName.apply(item),
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -61,7 +61,7 @@ public class UserDTO implements Serializable {
|
|||||||
private String sex;
|
private String sex;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 帐号状态(0正常 1停用)
|
* 账号状态(0正常 1停用)
|
||||||
*/
|
*/
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ public class UserOnlineDTO implements Serializable {
|
|||||||
private String deptName;
|
private String deptName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户名称
|
* 用户账号
|
||||||
*/
|
*/
|
||||||
private String userName;
|
private String userName;
|
||||||
|
|
||||||
|
|||||||
+34
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+36
-7
@@ -4,13 +4,13 @@ import lombok.Data;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 总体流程监听
|
* 总体流程监听
|
||||||
*
|
*
|
||||||
* @author may
|
* @author may
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ProcessEvent implements Serializable {
|
public class ProcessEvent implements Serializable {
|
||||||
|
|
||||||
@@ -18,24 +18,53 @@ public class ProcessEvent implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程定义key
|
* 租户ID
|
||||||
*/
|
*/
|
||||||
private String key;
|
private String tenantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程定义编码
|
||||||
|
*/
|
||||||
|
private String flowCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例id
|
||||||
|
*/
|
||||||
|
private Long instanceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务id
|
* 业务id
|
||||||
*/
|
*/
|
||||||
private String businessKey;
|
private String businessId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 状态
|
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||||
|
*/
|
||||||
|
private Integer nodeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程节点编码
|
||||||
|
*/
|
||||||
|
private String nodeCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程节点名称
|
||||||
|
*/
|
||||||
|
private String nodeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程状态
|
||||||
*/
|
*/
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理参数
|
||||||
|
*/
|
||||||
|
private Map<String, Object> params;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 当为true时为申请人节点办理
|
* 当为true时为申请人节点办理
|
||||||
*/
|
*/
|
||||||
private boolean submit;
|
private Boolean submit;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-8
@@ -4,13 +4,13 @@ import lombok.Data;
|
|||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程办理监听
|
* 流程任务监听
|
||||||
*
|
*
|
||||||
* @author may
|
* @author may
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class ProcessTaskEvent implements Serializable {
|
public class ProcessTaskEvent implements Serializable {
|
||||||
|
|
||||||
@@ -18,23 +18,53 @@ public class ProcessTaskEvent implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 流程定义key
|
* 租户ID
|
||||||
*/
|
*/
|
||||||
private String key;
|
private String tenantId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批节点key
|
* 流程定义编码
|
||||||
*/
|
*/
|
||||||
private String taskDefinitionKey;
|
private String flowCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
|
||||||
|
*/
|
||||||
|
private Integer nodeType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程节点编码
|
||||||
|
*/
|
||||||
|
private String nodeCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程节点名称
|
||||||
|
*/
|
||||||
|
private String nodeName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 任务id
|
* 任务id
|
||||||
*/
|
*/
|
||||||
private String taskId;
|
private Long taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实例id
|
||||||
|
*/
|
||||||
|
private Long instanceId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务id
|
* 业务id
|
||||||
*/
|
*/
|
||||||
private String businessKey;
|
private String businessId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程状态
|
||||||
|
*/
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理参数
|
||||||
|
*/
|
||||||
|
private Map<String, Object> params;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -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;
|
||||||
@@ -111,6 +112,11 @@ public class LoginUser implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private List<RoleDTO> roles;
|
private List<RoleDTO> roles;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位对象
|
||||||
|
*/
|
||||||
|
private List<PostDTO> posts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据权限 当前角色ID
|
* 数据权限 当前角色ID
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
-4
@@ -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,15 @@ 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}")
|
||||||
|
// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -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,16 +18,20 @@ 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}")
|
||||||
|
// @Pattern(regexp = RegexConstants.PASSWORD, message = "{user.password.format.valid}")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户类型
|
||||||
|
*/
|
||||||
private String userType;
|
private String userType;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+56
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
+71
-8
@@ -7,6 +7,10 @@ import org.dromara.common.core.exception.ServiceException;
|
|||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务状态枚举
|
* 业务状态枚举
|
||||||
@@ -16,30 +20,37 @@ import java.util.Arrays;
|
|||||||
@Getter
|
@Getter
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public enum BusinessStatusEnum {
|
public enum BusinessStatusEnum {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已撤销
|
* 已撤销
|
||||||
*/
|
*/
|
||||||
CANCEL("cancel", "已撤销"),
|
CANCEL("cancel", "已撤销"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 草稿
|
* 草稿
|
||||||
*/
|
*/
|
||||||
DRAFT("draft", "草稿"),
|
DRAFT("draft", "草稿"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待审核
|
* 待审核
|
||||||
*/
|
*/
|
||||||
WAITING("waiting", "待审核"),
|
WAITING("waiting", "待审核"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已完成
|
* 已完成
|
||||||
*/
|
*/
|
||||||
FINISH("finish", "已完成"),
|
FINISH("finish", "已完成"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已作废
|
* 已作废
|
||||||
*/
|
*/
|
||||||
INVALID("invalid", "已作废"),
|
INVALID("invalid", "已作废"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已退回
|
* 已退回
|
||||||
*/
|
*/
|
||||||
BACK("back", "已退回"),
|
BACK("back", "已退回"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 已终止
|
* 已终止
|
||||||
*/
|
*/
|
||||||
@@ -55,20 +66,72 @@ public enum BusinessStatusEnum {
|
|||||||
*/
|
*/
|
||||||
private final String desc;
|
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 状态
|
* @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) {
|
public static String findByStatus(String status) {
|
||||||
if (StringUtils.isBlank(status)) {
|
if (StringUtils.isBlank(status)) {
|
||||||
return StrUtil.EMPTY;
|
return StrUtil.EMPTY;
|
||||||
}
|
}
|
||||||
return Arrays.stream(BusinessStatusEnum.values())
|
BusinessStatusEnum statusEnum = STATUS_MAP.get(status);
|
||||||
.filter(statusEnum -> statusEnum.getStatus().equals(status))
|
return (statusEnum != null) ? statusEnum.getDesc() : StrUtil.EMPTY;
|
||||||
.findFirst()
|
}
|
||||||
.map(BusinessStatusEnum::getDesc)
|
|
||||||
.orElse(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -148,5 +211,5 @@ public enum BusinessStatusEnum {
|
|||||||
throw new ServiceException("流程状态为空!");
|
throw new ServiceException("流程状态为空!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+4
-2
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
+146
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
-30
@@ -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;
|
|
||||||
|
|
||||||
}
|
|
||||||
+7
-5
@@ -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) {
|
||||||
|
|||||||
+10
-2
@@ -1,11 +1,15 @@
|
|||||||
package org.dromara.common.core.exception;
|
package org.dromara.common.core.exception;
|
||||||
|
|
||||||
import lombok.*;
|
import cn.hutool.core.text.StrFormatter;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务异常
|
* 业务异常(支持占位符 {} )
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@@ -42,6 +46,10 @@ public final class ServiceException extends RuntimeException {
|
|||||||
this.code = code;
|
this.code = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceException(String message, Object... args) {
|
||||||
|
this.message = StrFormatter.format(message, args);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
|
|||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package org.dromara.common.core.exception;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sse 特制异常
|
||||||
|
*
|
||||||
|
* @author LionLi
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public final class SseException extends RuntimeException {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
*/
|
||||||
|
private String detailMessage;
|
||||||
|
|
||||||
|
public SseException(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SseException(String message, Integer code) {
|
||||||
|
this.message = message;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SseException setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SseException setDetailMessage(String detailMessage) {
|
||||||
|
this.detailMessage = detailMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+82
@@ -1,5 +1,11 @@
|
|||||||
package org.dromara.common.core.service;
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用 参数配置服务
|
* 通用 参数配置服务
|
||||||
*
|
*
|
||||||
@@ -15,4 +21,80 @@ public interface ConfigService {
|
|||||||
*/
|
*/
|
||||||
String getConfigValue(String configKey);
|
String getConfigValue(String configKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取布尔值
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return Boolean 值
|
||||||
|
*/
|
||||||
|
default Boolean getConfigBool(String configKey) {
|
||||||
|
return Convert.toBool(getConfigValue(configKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取整数值
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return Integer 值
|
||||||
|
*/
|
||||||
|
default Integer getConfigInt(String configKey) {
|
||||||
|
return Convert.toInt(getConfigValue(configKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取长整型值
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return Long 值
|
||||||
|
*/
|
||||||
|
default Long getConfigLong(String configKey) {
|
||||||
|
return Convert.toLong(getConfigValue(configKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取 BigDecimal 值
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return BigDecimal 值
|
||||||
|
*/
|
||||||
|
default BigDecimal getConfigDecimal(String configKey) {
|
||||||
|
return Convert.toBigDecimal(getConfigValue(configKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取 Map 类型的配置
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return Dict 对象,如果配置为空或无法解析,返回空 Dict
|
||||||
|
*/
|
||||||
|
Dict getConfigMap(String configKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取 Map 类型的配置列表
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @return Dict 列表,如果配置为空或无法解析,返回空列表
|
||||||
|
*/
|
||||||
|
List<Dict> getConfigArrayMap(String configKey);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取指定类型的配置对象
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @param clazz 目标对象类型
|
||||||
|
* @param <T> 目标对象泛型
|
||||||
|
* @return 对象实例,如果配置为空或无法解析,返回 null
|
||||||
|
*/
|
||||||
|
<T> T getConfigObject(String configKey, Class<T> clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据参数 key 获取指定类型的配置列表
|
||||||
|
*
|
||||||
|
* @param configKey 参数 key
|
||||||
|
* @param clazz 目标元素类型
|
||||||
|
* @param <T> 元素类型泛型
|
||||||
|
* @return 指定类型列表,如果配置为空或无法解析,返回空列表
|
||||||
|
*/
|
||||||
|
<T> List<T> getConfigArray(String configKey, Class<T> clazz);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -1,5 +1,10 @@
|
|||||||
package org.dromara.common.core.service;
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import org.dromara.common.core.domain.dto.DeptDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用 部门服务
|
* 通用 部门服务
|
||||||
*
|
*
|
||||||
@@ -15,4 +20,27 @@ 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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据部门 ID 列表查询部门名称映射关系
|
||||||
|
*
|
||||||
|
* @param deptIds 部门 ID 列表
|
||||||
|
* @return Map,其中 key 为部门 ID,value 为对应的部门名称
|
||||||
|
*/
|
||||||
|
Map<Long, String> selectDeptNamesByIds(List<Long> deptIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+20
@@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户权限处理
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public interface PermissionService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取角色数据权限
|
||||||
|
*
|
||||||
|
* @param userId 用户id
|
||||||
|
* @return 角色权限信息
|
||||||
|
*/
|
||||||
|
Set<String> getRolePermission(Long userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取菜单数据权限
|
||||||
|
*
|
||||||
|
* @param userId 用户id
|
||||||
|
* @return 菜单权限信息
|
||||||
|
*/
|
||||||
|
Set<String> getMenuPermission(Long userId);
|
||||||
|
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 岗位服务
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
public interface PostService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据岗位 ID 列表查询岗位名称映射关系
|
||||||
|
*
|
||||||
|
* @param postIds 岗位 ID 列表
|
||||||
|
* @return Map,其中 key 为岗位 ID,value 为对应的岗位名称
|
||||||
|
*/
|
||||||
|
Map<Long, String> selectPostNamesByIds(List<Long> postIds);
|
||||||
|
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package org.dromara.common.core.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用 角色服务
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
public interface RoleService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色 ID 列表查询角色名称映射关系
|
||||||
|
*
|
||||||
|
* @param roleIds 角色 ID 列表
|
||||||
|
* @return Map,其中 key 为角色 ID,value 为对应的角色名称
|
||||||
|
*/
|
||||||
|
Map<Long, String> selectRoleNamesByIds(List<Long> roleIds);
|
||||||
|
|
||||||
|
}
|
||||||
+45
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
+22
-4
@@ -3,6 +3,7 @@ package org.dromara.common.core.service;
|
|||||||
import org.dromara.common.core.domain.dto.UserDTO;
|
import org.dromara.common.core.domain.dto.UserDTO;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通用 用户服务
|
* 通用 用户服务
|
||||||
@@ -20,18 +21,18 @@ public interface UserService {
|
|||||||
String selectUserNameById(Long userId);
|
String selectUserNameById(Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过用户ID查询用户账户
|
* 通过用户ID查询用户昵称
|
||||||
*
|
*
|
||||||
* @param userId 用户ID
|
* @param userId 用户ID
|
||||||
* @return 用户名称
|
* @return 用户昵称
|
||||||
*/
|
*/
|
||||||
String selectNicknameById(Long userId);
|
String selectNicknameById(Long userId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过用户ID查询用户账户
|
* 通过用户ID查询用户昵称
|
||||||
*
|
*
|
||||||
* @param userIds 用户ID 多个用逗号隔开
|
* @param userIds 用户ID 多个用逗号隔开
|
||||||
* @return 用户名称
|
* @return 用户昵称
|
||||||
*/
|
*/
|
||||||
String selectNicknameByIds(String userIds);
|
String selectNicknameByIds(String userIds);
|
||||||
|
|
||||||
@@ -82,4 +83,21 @@ public interface UserService {
|
|||||||
* @return 用户
|
* @return 用户
|
||||||
*/
|
*/
|
||||||
List<UserDTO> selectUsersByDeptIds(List<Long> deptIds);
|
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> selectUserNicksByIds(List<Long> userIds);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-33
@@ -1,5 +1,9 @@
|
|||||||
package org.dromara.common.core.service;
|
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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -13,64 +17,89 @@ public interface WorkflowService {
|
|||||||
/**
|
/**
|
||||||
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
* 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息
|
||||||
*
|
*
|
||||||
* @param businessKeys 业务id
|
* @param businessIds 业务id
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
boolean deleteRunAndHisInstance(List<String> businessKeys);
|
boolean deleteInstance(List<String> businessIds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前流程状态
|
* 获取当前流程状态
|
||||||
*
|
*
|
||||||
* @param taskId 任务id
|
* @param taskId 任务id
|
||||||
|
* @return 状态
|
||||||
*/
|
*/
|
||||||
String getBusinessStatusByTaskId(String taskId);
|
String getBusinessStatusByTaskId(Long taskId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前流程状态
|
* 获取当前流程状态
|
||||||
*
|
*
|
||||||
* @param businessKey 业务id
|
* @param businessId 业务id
|
||||||
|
* @return 状态
|
||||||
*/
|
*/
|
||||||
String getBusinessStatus(String businessKey);
|
String getBusinessStatus(String businessId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置流程变量(全局变量)
|
* 设置流程变量
|
||||||
*
|
*
|
||||||
* @param taskId 任务id
|
* @param instanceId 流程实例id
|
||||||
* @param variableName 变量名称
|
* @param variable 流程变量
|
||||||
* @param value 变量值
|
|
||||||
*/
|
*/
|
||||||
void setVariable(String taskId, String variableName, Object value);
|
void setVariable(Long instanceId, Map<String, Object> variable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置流程变量(全局变量)
|
* 获取流程变量
|
||||||
*
|
*
|
||||||
* @param taskId 任务id
|
* @param instanceId 流程实例id
|
||||||
* @param variables 流程变量
|
|
||||||
*/
|
*/
|
||||||
void setVariables(String taskId, Map<String, Object> variables);
|
Map<String, Object> instanceVariable(Long instanceId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置流程变量(本地变量,非全局变量)
|
|
||||||
*
|
|
||||||
* @param taskId 任务id
|
|
||||||
* @param variableName 变量名称
|
|
||||||
* @param value 变量值
|
|
||||||
*/
|
|
||||||
void setVariableLocal(String taskId, String variableName, Object value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设置流程变量(本地变量,非全局变量)
|
|
||||||
*
|
|
||||||
* @param taskId 任务id
|
|
||||||
* @param variables 流程变量
|
|
||||||
*/
|
|
||||||
void setVariablesLocal(String taskId, Map<String, Object> variables);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 按照业务id查询流程实例id
|
* 按照业务id查询流程实例id
|
||||||
*
|
*
|
||||||
* @param businessKey 业务id
|
* @param businessId 业务id
|
||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
String getInstanceIdByBusinessKey(String businessKey);
|
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 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean completeTask(CompleteTaskDTO completeTask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 办理任务
|
||||||
|
*
|
||||||
|
* @param taskId 任务ID
|
||||||
|
* @param message 办理意见
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean completeTask(Long taskId, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动流程并办理第一个任务
|
||||||
|
*
|
||||||
|
* @param startProcess 参数
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
boolean startCompleteTask(StartProcessDTO startProcess);
|
||||||
}
|
}
|
||||||
|
|||||||
+281
-71
@@ -1,106 +1,159 @@
|
|||||||
package org.dromara.common.core.utils;
|
package org.dromara.common.core.utils;
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
import cn.hutool.core.date.DateUnit;
|
||||||
import lombok.NoArgsConstructor;
|
import cn.hutool.core.date.DateUtil;
|
||||||
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 +168,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 +177,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 +257,122 @@ 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定日期时间获取时间段(凌晨 / 上午 / 中午 / 下午 / 晚上)
|
||||||
|
*
|
||||||
|
* @param date 日期时间
|
||||||
|
* @return 时间段描述
|
||||||
|
*/
|
||||||
|
public static String getTodayHour(Date date) {
|
||||||
|
int hour = DateUtil.hour(date, true);
|
||||||
|
if (hour <= 6) {
|
||||||
|
return "凌晨";
|
||||||
|
} else if (hour < 12) {
|
||||||
|
return "上午";
|
||||||
|
} else if (hour == 12) {
|
||||||
|
return "中午";
|
||||||
|
} else if (hour <= 18) {
|
||||||
|
return "下午";
|
||||||
|
} else {
|
||||||
|
return "晚上";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将日期格式化为仿微信的友好时间
|
||||||
|
* <p>
|
||||||
|
* 规则说明:
|
||||||
|
* 1. 未来时间:yyyy-MM-dd HH:mm
|
||||||
|
* 2. 今天:
|
||||||
|
* - 1 分钟内:刚刚
|
||||||
|
* - 1 小时内:X 分钟前
|
||||||
|
* - 超过 1 小时:凌晨/上午/中午/下午/晚上 HH:mm
|
||||||
|
* 3. 昨天:昨天 HH:mm
|
||||||
|
* 4. 本周:周X HH:mm
|
||||||
|
* 5. 今年内:MM-dd HH:mm
|
||||||
|
* 6. 非今年:yyyy-MM-dd HH:mm
|
||||||
|
*
|
||||||
|
* @param date 日期时间
|
||||||
|
* @return 格式化后的时间描述
|
||||||
|
*/
|
||||||
|
public static String formatFriendlyTime(Date date) {
|
||||||
|
if (date == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
Date now = DateUtil.date();
|
||||||
|
|
||||||
|
// 未来时间或非今年
|
||||||
|
if (date.after(now) || DateUtil.year(date) != DateUtil.year(now)) {
|
||||||
|
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 今天
|
||||||
|
if (DateUtil.isSameDay(date, now)) {
|
||||||
|
long minutes = DateUtil.between(date, now, DateUnit.MINUTE);
|
||||||
|
if (minutes < 1) {
|
||||||
|
return "刚刚";
|
||||||
|
}
|
||||||
|
if (minutes < 60) {
|
||||||
|
return minutes + "分钟前";
|
||||||
|
}
|
||||||
|
return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 昨天
|
||||||
|
if (DateUtil.isSameDay(date, DateUtil.yesterday())) {
|
||||||
|
return "昨天 " + DateUtil.format(date, "HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 本周
|
||||||
|
if (DateUtil.isSameWeek(date, now, true)) {
|
||||||
|
return DateUtil.dayOfWeekEnum(date).toChinese("周")
|
||||||
|
+ " " + DateUtil.format(date, "HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 今年内其它时间
|
||||||
|
return DateUtil.format(date, "MM-dd HH:mm");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
package org.dromara.common.core.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.DesensitizedUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 脱敏工具类
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class DesensitizedUtils extends DesensitizedUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 灵活脱敏方法
|
||||||
|
*
|
||||||
|
* @param value 原始字符串
|
||||||
|
* @param prefixVisible 前面可见长度
|
||||||
|
* @param suffixVisible 后面可见长度
|
||||||
|
* @param maskLength 中间掩码长度(固定显示多少 *,如果总长度不足则自动缩减)
|
||||||
|
* @return 脱敏后字符串
|
||||||
|
*/
|
||||||
|
public static String mask(String value, int prefixVisible, int suffixVisible, int maskLength) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int len = value.length();
|
||||||
|
int prefixMaskLimit = prefixVisible + maskLength;
|
||||||
|
int fullLimit = prefixMaskLimit + suffixVisible;
|
||||||
|
|
||||||
|
// 规则 1:长度 <= 中间掩码长度 → 全掩码
|
||||||
|
if (len <= maskLength) {
|
||||||
|
return StrUtil.repeat('*', len);
|
||||||
|
}
|
||||||
|
String mask = StrUtil.repeat('*', maskLength);
|
||||||
|
|
||||||
|
// 规则 2:长度 <= 前缀 + 中间掩码
|
||||||
|
if (len <= prefixMaskLimit) {
|
||||||
|
return value.substring(0, len - maskLength) + mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
String prefix = value.substring(0, prefixVisible);
|
||||||
|
|
||||||
|
// 规则 3:长度 <= 前缀 + 中间掩码 + 后缀
|
||||||
|
if (len <= fullLimit) {
|
||||||
|
int suffixLen = len - prefixMaskLimit;
|
||||||
|
return prefix + mask + value.substring(len - suffixLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则 4:标准形态
|
||||||
|
return prefix + mask + value.substring(len - suffixVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 高安全级别脱敏方法(Token / 私钥)
|
||||||
|
*
|
||||||
|
* @param value 原始字符串
|
||||||
|
* @param prefixVisible 前面可见长度(推荐0~4)
|
||||||
|
* @param suffixVisible 后面可见长度(推荐0~4)
|
||||||
|
* @return 脱敏后字符串
|
||||||
|
*/
|
||||||
|
public static String maskHighSecurity(String value, int prefixVisible, int suffixVisible) {
|
||||||
|
if (StrUtil.isBlank(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
int len = value.length();
|
||||||
|
|
||||||
|
// 规则1:长度 <= 前缀可见长度 → 全部掩码
|
||||||
|
if (len <= prefixVisible) {
|
||||||
|
return StrUtil.repeat('*', len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则2:长度 <= 前缀 + 后缀可见长度 → 优先掩码后面
|
||||||
|
if (len <= prefixVisible + suffixVisible) {
|
||||||
|
return value.substring(0, len - prefixVisible) + StrUtil.repeat('*', prefixVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 规则3:标准形态 → 前后可见,中间全部掩码
|
||||||
|
return value.substring(0, prefixVisible)
|
||||||
|
+ StrUtil.repeat('*', len - prefixVisible - suffixVisible)
|
||||||
|
+ value.substring(len - suffixVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+84
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+60
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+81
-20
@@ -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,21 +107,23 @@ 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<>();
|
||||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
|
params.put(entry.getKey(), StringUtils.joinComma(entry.getValue()));
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取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 解码后的内容
|
||||||
|
|||||||
+89
-44
@@ -7,7 +7,6 @@ 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;
|
||||||
@@ -31,8 +30,10 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return CollUtil.newArrayList();
|
return CollUtil.newArrayList();
|
||||||
}
|
}
|
||||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
return collection.stream()
|
||||||
return collection.stream().filter(function).collect(Collectors.toList());
|
.filter(function)
|
||||||
|
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,13 +41,26 @@ public class StreamUtils {
|
|||||||
*
|
*
|
||||||
* @param collection 需要查询的集合
|
* @param collection 需要查询的集合
|
||||||
* @param function 过滤方法
|
* @param function 过滤方法
|
||||||
* @return 找到符合条件的第一个元素,没有则返回null
|
* @return 找到符合条件的第一个元素,没有则返回 Optional.empty()
|
||||||
*/
|
*/
|
||||||
public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
|
public static <E> Optional<E> findFirst(Collection<E> collection, Predicate<E> function) {
|
||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return null;
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return collection.stream().filter(function).findFirst().orElse(null);
|
return collection.stream()
|
||||||
|
.filter(function)
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到流中满足条件的第一个元素值
|
||||||
|
*
|
||||||
|
* @param collection 需要查询的集合
|
||||||
|
* @param function 过滤方法
|
||||||
|
* @return 找到符合条件的第一个元素,没有则返回 null
|
||||||
|
*/
|
||||||
|
public static <E> E findFirstValue(Collection<E> collection, Predicate<E> function) {
|
||||||
|
return findFirst(collection,function).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,13 +68,26 @@ public class StreamUtils {
|
|||||||
*
|
*
|
||||||
* @param collection 需要查询的集合
|
* @param collection 需要查询的集合
|
||||||
* @param function 过滤方法
|
* @param function 过滤方法
|
||||||
* @return 找到符合条件的任意一个元素,没有则返回null
|
* @return 找到符合条件的任意一个元素,没有则返回 Optional.empty()
|
||||||
*/
|
*/
|
||||||
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
|
public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
|
||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return collection.stream().filter(function).findAny();
|
return collection.stream()
|
||||||
|
.filter(function)
|
||||||
|
.findAny();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找到流中任意一个满足条件的元素值
|
||||||
|
*
|
||||||
|
* @param collection 需要查询的集合
|
||||||
|
* @param function 过滤方法
|
||||||
|
* @return 找到符合条件的任意一个元素,没有则返回null
|
||||||
|
*/
|
||||||
|
public static <E> E findAnyValue(Collection<E> collection, Predicate<E> function) {
|
||||||
|
return findAny(collection,function).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,7 +113,10 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return StringUtils.EMPTY;
|
return StringUtils.EMPTY;
|
||||||
}
|
}
|
||||||
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
|
return collection.stream()
|
||||||
|
.map(function)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.joining(delimiter));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,8 +130,11 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return CollUtil.newArrayList();
|
return CollUtil.newArrayList();
|
||||||
}
|
}
|
||||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
return collection.stream()
|
||||||
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
|
.filter(Objects::nonNull)
|
||||||
|
.sorted(comparing)
|
||||||
|
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,7 +151,9 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
}
|
}
|
||||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
|
return collection.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +172,25 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
}
|
}
|
||||||
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
|
return collection.stream()
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(key, value, (l, r) -> l));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 map 中的数据作为新 Map 的 value ,key 不变
|
||||||
|
* @param map 需要处理的map
|
||||||
|
* @param take 取值函数
|
||||||
|
* @param <K> map中的key类型
|
||||||
|
* @param <E> map中的value类型
|
||||||
|
* @param <V> 新map中的value类型
|
||||||
|
* @return 新的map
|
||||||
|
*/
|
||||||
|
public static <K, E, V> Map<K, V> toMap(Map<K, E> map, BiFunction<K, E, V> take) {
|
||||||
|
if (CollUtil.isEmpty(map)) {
|
||||||
|
return MapUtil.newHashMap();
|
||||||
|
}
|
||||||
|
return toMap(map.entrySet(), Map.Entry::getKey, entry -> take.apply(entry.getKey(), entry.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,8 +207,8 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
}
|
}
|
||||||
return collection
|
return collection.stream()
|
||||||
.stream().filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
|
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +228,8 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
}
|
}
|
||||||
return collection
|
return collection.stream()
|
||||||
.stream().filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
|
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,11 +246,11 @@ public class StreamUtils {
|
|||||||
* @return 分类后的map
|
* @return 分类后的map
|
||||||
*/
|
*/
|
||||||
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
|
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
|
||||||
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
}
|
}
|
||||||
return collection
|
return collection.stream()
|
||||||
.stream().filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
|
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,8 +268,7 @@ public class StreamUtils {
|
|||||||
if (CollUtil.isEmpty(collection)) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return CollUtil.newArrayList();
|
return CollUtil.newArrayList();
|
||||||
}
|
}
|
||||||
return collection
|
return collection.stream()
|
||||||
.stream()
|
|
||||||
.map(function)
|
.map(function)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
// 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
|
||||||
@@ -234,11 +286,10 @@ public class StreamUtils {
|
|||||||
* @return 转化后的Set
|
* @return 转化后的Set
|
||||||
*/
|
*/
|
||||||
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
|
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
|
||||||
if (CollUtil.isEmpty(collection) || function == null) {
|
if (CollUtil.isEmpty(collection)) {
|
||||||
return CollUtil.newHashSet();
|
return CollUtil.newHashSet();
|
||||||
}
|
}
|
||||||
return collection
|
return collection.stream()
|
||||||
.stream()
|
|
||||||
.map(function)
|
.map(function)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
@@ -258,26 +309,20 @@ public class StreamUtils {
|
|||||||
* @return 合并后的map
|
* @return 合并后的map
|
||||||
*/
|
*/
|
||||||
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
|
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
|
||||||
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
|
if (CollUtil.isEmpty(map1) && CollUtil.isEmpty(map2)) {
|
||||||
|
// 如果两个 map 都为空,则直接返回空的 map
|
||||||
return MapUtil.newHashMap();
|
return MapUtil.newHashMap();
|
||||||
} else if (MapUtil.isEmpty(map1)) {
|
} else if (CollUtil.isEmpty(map1)) {
|
||||||
map1 = MapUtil.newHashMap();
|
// 如果 map1 为空,则直接处理返回 map2
|
||||||
} else if (MapUtil.isEmpty(map2)) {
|
return toMap(map2.entrySet(), Map.Entry::getKey, entry -> merge.apply(null, entry.getValue()));
|
||||||
map2 = MapUtil.newHashMap();
|
} else if (CollUtil.isEmpty(map2)) {
|
||||||
|
// 如果 map2 为空,则直接处理返回 map1
|
||||||
|
return toMap(map1.entrySet(), Map.Entry::getKey, entry -> merge.apply(entry.getValue(), null));
|
||||||
}
|
}
|
||||||
Set<K> key = new HashSet<>();
|
Set<K> keySet = new HashSet<>();
|
||||||
key.addAll(map1.keySet());
|
keySet.addAll(map1.keySet());
|
||||||
key.addAll(map2.keySet());
|
keySet.addAll(map2.keySet());
|
||||||
Map<K, V> map = new HashMap<>();
|
return toMap(keySet, key -> key, key -> merge.apply(map1.get(key), map2.get(key)));
|
||||||
for (K t : key) {
|
|
||||||
X x = map1.get(t);
|
|
||||||
Y y = map2.get(t);
|
|
||||||
V z = merge.apply(x, y);
|
|
||||||
if (z != null) {
|
|
||||||
map.put(t, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-5
@@ -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,13 +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 = "/";
|
public static final String SLASH = "/";
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
private StringUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取参数不为空值
|
* 获取参数不为空值
|
||||||
*
|
*
|
||||||
@@ -258,13 +260,13 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
|||||||
if (s != null) {
|
if (s != null) {
|
||||||
final int len = s.length();
|
final int len = s.length();
|
||||||
if (s.length() <= size) {
|
if (s.length() <= size) {
|
||||||
sb.append(String.valueOf(c).repeat(size - len));
|
sb.append(Convert.toStr(c).repeat(size - len));
|
||||||
sb.append(s);
|
sb.append(s);
|
||||||
} else {
|
} else {
|
||||||
return s.substring(len - size, len);
|
return s.substring(len - size, len);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sb.append(String.valueOf(c).repeat(Math.max(0, size)));
|
sb.append(Convert.toStr(c).repeat(Math.max(0, size)));
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
@@ -317,7 +319,66 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 将可迭代对象中的元素使用逗号拼接成字符串
|
||||||
|
*
|
||||||
|
* @param iterable 可迭代对象,如 List、Set 等
|
||||||
|
* @return 拼接后的字符串
|
||||||
|
*/
|
||||||
|
public static String joinComma(Iterable<?> iterable) {
|
||||||
|
return StringUtils.join(iterable, SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将数组中的元素使用逗号拼接成字符串
|
||||||
|
*
|
||||||
|
* @param array 任意类型的数组
|
||||||
|
* @return 拼接后的字符串
|
||||||
|
*/
|
||||||
|
public static String joinComma(Object[] array) {
|
||||||
|
return StringUtils.join(array, SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-75
@@ -1,75 +0,0 @@
|
|||||||
package org.dromara.common.core.utils;
|
|
||||||
|
|
||||||
import lombok.AccessLevel;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 线程相关工具类.
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
|
||||||
public class Threads {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* sleep等待,单位为毫秒
|
|
||||||
*/
|
|
||||||
public static void sleep(long milliseconds) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(milliseconds);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 停止线程池
|
|
||||||
* 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务.
|
|
||||||
* 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数.
|
|
||||||
* 如果仍然超時,則強制退出.
|
|
||||||
* 另对在shutdown时线程本身被调用中断做了处理.
|
|
||||||
*/
|
|
||||||
public static void shutdownAndAwaitTermination(ExecutorService pool) {
|
|
||||||
if (pool != null && !pool.isShutdown()) {
|
|
||||||
pool.shutdown();
|
|
||||||
try {
|
|
||||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
|
||||||
pool.shutdownNow();
|
|
||||||
if (!pool.awaitTermination(120, TimeUnit.SECONDS)) {
|
|
||||||
log.info("Pool did not terminate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
pool.shutdownNow();
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 打印线程异常信息
|
|
||||||
*/
|
|
||||||
public static void printException(Runnable r, Throwable t) {
|
|
||||||
if (t == null && r instanceof Future<?>) {
|
|
||||||
try {
|
|
||||||
Future<?> future = (Future<?>) r;
|
|
||||||
if (future.isDone()) {
|
|
||||||
future.get();
|
|
||||||
}
|
|
||||||
} catch (CancellationException ce) {
|
|
||||||
t = ce;
|
|
||||||
} catch (ExecutionException ee) {
|
|
||||||
t = ee.getCause();
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (t != null) {
|
|
||||||
log.error(t.getMessage(), t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+90
-2
@@ -5,11 +5,15 @@ 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.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 扩展 hutool TreeUtil 封装系统树构建
|
* 扩展 hutool TreeUtil 封装系统树构建
|
||||||
@@ -24,12 +28,96 @@ 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 list 原始数据列表
|
||||||
|
* @param getId 获取节点 ID 的方法引用,例如:node -> node.getId()
|
||||||
|
* @param getParentId 获取节点父级 ID 的方法引用,例如:node -> node.getParentId()
|
||||||
|
* @param parser 树节点属性映射器,用于将原始节点 T 转为 Tree 节点
|
||||||
|
* @param <T> 原始数据类型(如实体类、DTO 等)
|
||||||
|
* @param <K> 节点 ID 类型(如 Long、String)
|
||||||
|
* @return 构建完成的树形结构(可能包含多个顶级根节点)
|
||||||
|
*/
|
||||||
|
public static <T, K> List<Tree<K>> buildMultiRoot(List<T> list, Function<T, K> getId, Function<T, K> getParentId, NodeParser<T, K> parser) {
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return CollUtil.newArrayList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<K> rootParentIds = StreamUtils.toSet(list, getParentId);
|
||||||
|
rootParentIds.removeAll(StreamUtils.toSet(list, getId));
|
||||||
|
|
||||||
|
// 构建每一个根 parentId 下的树,并合并成最终结果列表
|
||||||
|
return rootParentIds.stream()
|
||||||
|
.flatMap(rootParentId -> TreeUtil.build(list, rootParentId, parser).stream())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取节点列表中所有节点的叶子节点
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-10
@@ -1,11 +1,11 @@
|
|||||||
package org.dromara.common.core.utils.ip;
|
package org.dromara.common.core.utils.ip;
|
||||||
|
|
||||||
import cn.hutool.core.net.NetUtil;
|
|
||||||
import cn.hutool.http.HtmlUtil;
|
import cn.hutool.http.HtmlUtil;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.core.utils.NetUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取地址类
|
* 获取地址类
|
||||||
@@ -16,18 +16,28 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class AddressUtils {
|
public class AddressUtils {
|
||||||
|
|
||||||
// 未知地址
|
// 未知IP
|
||||||
public static final String UNKNOWN = "XX XX";
|
public static final String UNKNOWN_IP = "XX XX";
|
||||||
|
// 内网地址
|
||||||
|
public static final String LOCAL_ADDRESS = "内网IP";
|
||||||
|
|
||||||
public static String getRealAddressByIP(String ip) {
|
public static String getRealAddressByIP(String ip) {
|
||||||
if (StringUtils.isBlank(ip)) {
|
// 处理空串并过滤HTML标签
|
||||||
return UNKNOWN;
|
ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
|
||||||
|
// 判断是否为IPv4
|
||||||
|
boolean isIPv4 = NetUtils.isIPv4(ip);
|
||||||
|
// 判断是否为IPv6
|
||||||
|
boolean isIPv6 = NetUtils.isIPv6(ip);
|
||||||
|
// 如果不是IPv4或IPv6,则返回未知IP
|
||||||
|
if (!isIPv4 && !isIPv6) {
|
||||||
|
return UNKNOWN_IP;
|
||||||
}
|
}
|
||||||
// 内网不查询
|
// 内网不查询
|
||||||
ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
|
if ((isIPv4 && NetUtils.isInnerIP(ip)) || (isIPv6 && NetUtils.isInnerIPv6(ip))) {
|
||||||
if (NetUtil.isInnerIP(ip)) {
|
return LOCAL_ADDRESS;
|
||||||
return "内网IP";
|
|
||||||
}
|
}
|
||||||
return RegionUtils.getCityInfo(ip);
|
// Tips:Ip2Region 提供了精简的IPv6地址库,精简的IPv6地址库并不能完全支持IPv6地址的查询,且准确度上可能会存在问题,如需要准确的IPv6地址查询,建议自行实现
|
||||||
|
return RegionUtils.getRegion(ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+127
-39
@@ -1,66 +1,154 @@
|
|||||||
package org.dromara.common.core.utils.ip;
|
package org.dromara.common.core.utils.ip;
|
||||||
|
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
import cn.hutool.core.io.resource.ClassPathResource;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
|
||||||
import org.dromara.common.core.utils.file.FileUtils;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.lionsoul.ip2region.service.Config;
|
||||||
|
import org.lionsoul.ip2region.service.Ip2Region;
|
||||||
|
import org.lionsoul.ip2region.xdb.Util;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.InputStream;
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据ip地址定位工具类,离线方式
|
* IP地址行政区域工具类
|
||||||
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
|
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region xdb java 查询客户端实现</a>
|
||||||
|
* xdb数据库文件下载:<a href="https://gitee.com/lionsoul/ip2region/tree/master/data">ip2region data</a>
|
||||||
*
|
*
|
||||||
* @author lishuyan
|
* @author 秋辞未寒
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class RegionUtils {
|
public class RegionUtils {
|
||||||
|
|
||||||
private static final Searcher SEARCHER;
|
// 默认IPv4地址库文件路径
|
||||||
|
// 下载地址:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb
|
||||||
|
public static final String DEFAULT_IPV4_XDB_PATH = "ip2region_v4.xdb";
|
||||||
|
|
||||||
|
// 默认IPv6地址库文件路径
|
||||||
|
// 下载地址:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
|
||||||
|
public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
|
||||||
|
|
||||||
|
// 默认缓存切片大小为15MB(仅针对BufferCache全量读取有效,如果你的xdb数据库很大,合理设置该值可以有效提升BufferCache模式下的查询效率,具体可以查看Ip2Region的README)
|
||||||
|
// 注意:设置过大的值可能会申请内存时,因内存不足而导致OOM,请合理设置该值。
|
||||||
|
// README:https://gitee.com/lionsoul/ip2region/tree/master/binding/java
|
||||||
|
public static final int DEFAULT_CACHE_SLICE_BYTES = 1024 * 1024 * 15;
|
||||||
|
|
||||||
|
// 未知地址
|
||||||
|
public static final String UNKNOWN_ADDRESS = "未知";
|
||||||
|
|
||||||
|
// Ip2Region服务实例
|
||||||
|
private static Ip2Region ip2Region;
|
||||||
|
|
||||||
|
// 初始化Ip2Region服务实例
|
||||||
static {
|
static {
|
||||||
String fileName = "/ip2region.xdb";
|
try {
|
||||||
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
|
// 注意:Ip2Region 的xdb文件加载策略 CachePolicy 有三种,分别是:BufferCache(全量读取xdb到内存中)、VIndexCache(默认策略,按需读取并缓存)、NoCache(实时读取)
|
||||||
if (!FileUtils.exist(existFile)) {
|
// 本项目工具使用的 CachePolicy 为 BufferCache,BufferCache会加载整个xdb文件到内存中,setXdbInputStream 仅支持 BufferCache 策略。
|
||||||
ClassPathResource fileStream = new ClassPathResource(fileName);
|
// 因为加载整个xdb文件会耗费非常大的内存,如果你不希望加载整个xdb到内存中,更推荐使用 VIndexCache 或 NoCache(即实时读取文件)策略和 setXdbPath/setXdbFile 加载方法(需要注意的一点,setXdbPath 和 setXdbFile 不支持读取ClassPath(即源码和resource目录)中的文件)。
|
||||||
if (ObjectUtil.isEmpty(fileStream.getStream())) {
|
// 一般而言,更建议把xdb数据库放到一个指定的文件目录中(即不打包进jar包中),然后使用 VIndexCache + 配合SearcherPool的并发池读取数据,更方便随时更新xdb数据库
|
||||||
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
|
|
||||||
|
InputStream v4InputStream = ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH);
|
||||||
|
|
||||||
|
// IPv4配置
|
||||||
|
Config v4Config = Config.custom()
|
||||||
|
.setCachePolicy(Config.BufferCache)
|
||||||
|
//.setXdbFile(v4TempXdb)
|
||||||
|
.setXdbInputStream(v4InputStream)
|
||||||
|
//
|
||||||
|
.setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
|
||||||
|
.asV4();
|
||||||
|
|
||||||
|
// IPv6配置
|
||||||
|
Config v6Config = null;
|
||||||
|
InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
|
||||||
|
if (v6XdbInputStream == null) {
|
||||||
|
log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6,请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH);
|
||||||
|
} else {
|
||||||
|
v6Config = Config.custom()
|
||||||
|
.setCachePolicy(Config.BufferCache)
|
||||||
|
//.setXdbFile(v6TempXdb)
|
||||||
|
.setXdbInputStream(v6XdbInputStream)
|
||||||
|
.setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
|
||||||
|
.asV6();
|
||||||
}
|
}
|
||||||
FileUtils.writeFromStream(fileStream.getStream(), existFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
String dbPath = existFile.getPath();
|
// 初始化Ip2Region实例
|
||||||
|
RegionUtils.ip2Region = Ip2Region.create(v4Config, v6Config);
|
||||||
// 1、从 dbPath 加载整个 xdb 到内存。
|
log.debug("IP工具初始化成功,加载IP地址库数据成功!");
|
||||||
byte[] cBuff;
|
|
||||||
try {
|
|
||||||
cBuff = Searcher.loadContentFromFile(dbPath);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
|
throw new ServiceException("RegionUtils初始化失败,原因:{}", e.getMessage());
|
||||||
}
|
|
||||||
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
|
||||||
try {
|
|
||||||
SEARCHER = Searcher.newWithBuffer(cBuff);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据IP地址离线获取城市
|
* 根据IP地址离线获取城市
|
||||||
|
*
|
||||||
|
* @param ipString ip地址字符串
|
||||||
*/
|
*/
|
||||||
public static String getCityInfo(String ip) {
|
public static String getRegion(String ipString) {
|
||||||
try {
|
try {
|
||||||
ip = ip.trim();
|
String region = ip2Region.search(ipString);
|
||||||
// 3、执行查询
|
if (StringUtils.isBlank(region)) {
|
||||||
String region = SEARCHER.search(ip);
|
return UNKNOWN_ADDRESS;
|
||||||
return region.replace("0|", "").replace("|0", "");
|
}
|
||||||
|
return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("IP地址离线获取城市异常 {}", ip);
|
log.error("IP地址离线获取城市异常 {}", ipString);
|
||||||
return "未知";
|
return UNKNOWN_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据IP地址离线获取城市
|
||||||
|
*
|
||||||
|
* @param ipBytes ip地址字节数组
|
||||||
|
*/
|
||||||
|
public static String getRegion(byte[] ipBytes) {
|
||||||
|
try {
|
||||||
|
String region = ip2Region.search(ipBytes);
|
||||||
|
if (StringUtils.isBlank(region)) {
|
||||||
|
return UNKNOWN_ADDRESS;
|
||||||
|
}
|
||||||
|
return StringUtils.replace(region, "0", UNKNOWN_ADDRESS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
|
||||||
|
return UNKNOWN_ADDRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭Ip2Region服务
|
||||||
|
*/
|
||||||
|
public static void close() {
|
||||||
|
if (ip2Region == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ip2Region.close(10000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Ip2Region服务关闭异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭Ip2Region服务
|
||||||
|
*
|
||||||
|
* @param timeout 关闭超时时间
|
||||||
|
*/
|
||||||
|
public static void close(final Duration timeout) {
|
||||||
|
if (ip2Region == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (timeout == null) {
|
||||||
|
close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
ip2Region.close(timeout.toMillis());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Ip2Region服务关闭异常", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -21,7 +21,8 @@ public final class RegexUtils extends ReUtil {
|
|||||||
*/
|
*/
|
||||||
public static String extractFromString(String input, String regex, String defaultInput) {
|
public static String extractFromString(String input, String regex, String defaultInput) {
|
||||||
try {
|
try {
|
||||||
return ReUtil.get(regex, input, 1);
|
String str = ReUtil.get(regex, input, 1);
|
||||||
|
return str == null ? defaultInput : str;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return defaultInput;
|
return defaultInput;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-4
@@ -1,5 +1,6 @@
|
|||||||
package org.dromara.common.core.utils.sql;
|
package org.dromara.common.core.utils.sql;
|
||||||
|
|
||||||
|
import cn.hutool.core.exceptions.UtilException;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
@@ -7,7 +8,7 @@ import org.dromara.common.core.utils.StringUtils;
|
|||||||
/**
|
/**
|
||||||
* sql操作工具类
|
* sql操作工具类
|
||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
public class SqlUtil {
|
public class SqlUtil {
|
||||||
@@ -15,7 +16,7 @@ public class SqlUtil {
|
|||||||
/**
|
/**
|
||||||
* 定义常用的 sql关键字
|
* 定义常用的 sql关键字
|
||||||
*/
|
*/
|
||||||
public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
|
public static final String SQL_REGEX = "\u000B|%0A|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||||
@@ -46,11 +47,21 @@ public class SqlUtil {
|
|||||||
if (StringUtils.isEmpty(value)) {
|
if (StringUtils.isEmpty(value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 核心增强:自动转义单引号 ====================
|
||||||
|
// 不抛异常、不破坏业务、不改变原方法行为、自动防注入
|
||||||
|
if (value.contains("'")) {
|
||||||
|
throw new UtilException("请求参数包含非法字符【'】,已禁止执行");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 原有逻辑不变 ====================
|
||||||
|
String normalizedValue = value.replaceAll("\\p{Z}|\\s", "");
|
||||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||||
for (String sqlKeyword : sqlKeywords) {
|
for (String sqlKeyword : sqlKeywords) {
|
||||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
if (StringUtils.indexOf(normalizedValue, sqlKeyword) > -1) {
|
||||||
throw new IllegalArgumentException("参数存在SQL注入风险");
|
throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package org.dromara.common.core.validate.dicts;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典项校验注解
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
@Constraint(validatedBy = DictPatternValidator.class)
|
||||||
|
@Target({ElementType.FIELD, ElementType.PARAMETER})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface DictPattern {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典类型,如 "sys_user_sex"
|
||||||
|
*/
|
||||||
|
String dictType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分隔符
|
||||||
|
*/
|
||||||
|
String separator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认校验失败提示信息
|
||||||
|
*/
|
||||||
|
String message() default "字典值无效";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package org.dromara.common.core.validate.dicts;
|
||||||
|
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
import org.dromara.common.core.service.DictService;
|
||||||
|
import org.dromara.common.core.utils.SpringUtils;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义字典值校验器
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
public class DictPatternValidator implements ConstraintValidator<DictPattern, String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典类型
|
||||||
|
*/
|
||||||
|
private String dictType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分隔符
|
||||||
|
*/
|
||||||
|
private String separator = ",";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化校验器,提取注解上的字典类型
|
||||||
|
*
|
||||||
|
* @param annotation 注解实例
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void initialize(DictPattern annotation) {
|
||||||
|
this.dictType = annotation.dictType();
|
||||||
|
if (StringUtils.isNotBlank(annotation.separator())) {
|
||||||
|
this.separator = annotation.separator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验字段值是否为指定字典类型中的合法值
|
||||||
|
*
|
||||||
|
* @param value 被校验的字段值
|
||||||
|
* @param context 校验上下文(可用于构建错误信息)
|
||||||
|
* @return true 表示校验通过(合法字典值),false 表示不通过
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||||
|
if (StringUtils.isBlank(dictType) || StringUtils.isBlank(value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String dictLabel = SpringUtils.getBean(DictService.class).getDictLabel(dictType, value, separator);
|
||||||
|
return StringUtils.isNotBlank(dictLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package org.dromara.common.core.validate.enumd;
|
||||||
|
|
||||||
|
import jakarta.validation.Constraint;
|
||||||
|
import jakarta.validation.Payload;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
import static java.lang.annotation.ElementType.*;
|
||||||
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义枚举校验
|
||||||
|
*
|
||||||
|
* @author 秋辞未寒
|
||||||
|
* @date 2024-12-09
|
||||||
|
*/
|
||||||
|
@Documented
|
||||||
|
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@Repeatable(EnumPattern.List.class) // 允许在同一元素上多次使用该注解
|
||||||
|
@Constraint(validatedBy = {EnumPatternValidator.class})
|
||||||
|
public @interface EnumPattern {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要校验的枚举类型
|
||||||
|
*/
|
||||||
|
Class<? extends Enum<?>> type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 枚举类型校验值字段名称
|
||||||
|
* 需确保该字段实现了 getter 方法
|
||||||
|
*/
|
||||||
|
String fieldName();
|
||||||
|
|
||||||
|
String message() default "输入值不在枚举范围内";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
|
||||||
|
@Retention(RUNTIME)
|
||||||
|
@interface List {
|
||||||
|
EnumPattern[] value();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package org.dromara.common.core.validate.enumd;
|
||||||
|
|
||||||
|
import jakarta.validation.ConstraintValidator;
|
||||||
|
import jakarta.validation.ConstraintValidatorContext;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义枚举校验注解实现
|
||||||
|
*
|
||||||
|
* @author 秋辞未寒
|
||||||
|
* @date 2024-12-09
|
||||||
|
*/
|
||||||
|
public class EnumPatternValidator implements ConstraintValidator<EnumPattern, String> {
|
||||||
|
|
||||||
|
private EnumPattern annotation;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(EnumPattern annotation) {
|
||||||
|
ConstraintValidator.super.initialize(annotation);
|
||||||
|
this.annotation = annotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
|
||||||
|
if (StringUtils.isNotBlank(value)) {
|
||||||
|
String fieldName = annotation.fieldName();
|
||||||
|
for (Object e : annotation.type().getEnumConstants()) {
|
||||||
|
if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
-2
@@ -1,6 +1,4 @@
|
|||||||
org.dromara.common.core.config.ApplicationConfig
|
org.dromara.common.core.config.ApplicationConfig
|
||||||
org.dromara.common.core.config.AsyncConfig
|
|
||||||
org.dromara.common.core.config.RuoYiConfig
|
|
||||||
org.dromara.common.core.config.ThreadPoolConfig
|
org.dromara.common.core.config.ThreadPoolConfig
|
||||||
org.dromara.common.core.config.ValidatorConfig
|
org.dromara.common.core.config.ValidatorConfig
|
||||||
org.dromara.common.core.utils.SpringUtils
|
org.dromara.common.core.utils.SpringUtils
|
||||||
|
|||||||
+23
-11
@@ -7,6 +7,8 @@ import io.swagger.v3.oas.models.security.SecurityRequirement;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
import org.dromara.common.doc.config.properties.SpringDocProperties;
|
||||||
|
import org.dromara.common.doc.core.resolver.JavadocResolver;
|
||||||
|
import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver;
|
||||||
import org.dromara.common.doc.handler.OpenApiHandler;
|
import org.dromara.common.doc.handler.OpenApiHandler;
|
||||||
import org.springdoc.core.configuration.SpringDocConfiguration;
|
import org.springdoc.core.configuration.SpringDocConfiguration;
|
||||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||||
@@ -30,7 +32,7 @@ import java.util.Optional;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Swagger 文档配置
|
* 接口文档配置
|
||||||
*
|
*
|
||||||
* @author Lion Li
|
* @author Lion Li
|
||||||
*/
|
*/
|
||||||
@@ -54,14 +56,15 @@ public class SpringDocConfig {
|
|||||||
openApi.externalDocs(properties.getExternalDocs());
|
openApi.externalDocs(properties.getExternalDocs());
|
||||||
openApi.tags(properties.getTags());
|
openApi.tags(properties.getTags());
|
||||||
openApi.paths(properties.getPaths());
|
openApi.paths(properties.getPaths());
|
||||||
openApi.components(properties.getComponents());
|
if (properties.getComponents() != null) {
|
||||||
Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
|
openApi.components(properties.getComponents());
|
||||||
List<SecurityRequirement> list = new ArrayList<>();
|
Set<String> keySet = properties.getComponents().getSecuritySchemes().keySet();
|
||||||
SecurityRequirement securityRequirement = new SecurityRequirement();
|
List<SecurityRequirement> list = new ArrayList<>();
|
||||||
keySet.forEach(securityRequirement::addList);
|
SecurityRequirement securityRequirement = new SecurityRequirement();
|
||||||
list.add(securityRequirement);
|
keySet.forEach(securityRequirement::addList);
|
||||||
openApi.security(list);
|
list.add(securityRequirement);
|
||||||
|
openApi.security(list);
|
||||||
|
}
|
||||||
return openApi;
|
return openApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +86,9 @@ public class SpringDocConfig {
|
|||||||
SecurityService securityParser,
|
SecurityService securityParser,
|
||||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
|
||||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
|
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider,
|
||||||
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
|
List<JavadocResolver> javadocResolvers) {
|
||||||
|
return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, javadocResolvers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,6 +115,14 @@ public class SpringDocConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册SaToken JavaDoc权限注解解析器
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public JavadocResolver saTokenAnnotationJavadocResolver() {
|
||||||
|
return new SaTokenAnnotationMetadataJavadocResolver();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单独使用一个类便于判断 解决springdoc路径拼接重复问题
|
* 单独使用一个类便于判断 解决springdoc路径拼接重复问题
|
||||||
*
|
*
|
||||||
|
|||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
package org.dromara.common.doc.core.model;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储权限框架注解解析后的权限和角色信息
|
||||||
|
*
|
||||||
|
* @author AprilWind
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonInclude(Include.NON_EMPTY)
|
||||||
|
public class SaTokenSecurityMetadata {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限校验信息列表(对应 @SaCheckPermission 注解)
|
||||||
|
*/
|
||||||
|
private List<AuthInfo> permissions = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色校验信息列表(对应 @SaCheckRole 注解)
|
||||||
|
*/
|
||||||
|
private List<AuthInfo> roles = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否忽略校验(对应 @SaIgnore 注解)
|
||||||
|
*/
|
||||||
|
private boolean ignore = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加权限信息
|
||||||
|
*
|
||||||
|
* @param values 权限值数组
|
||||||
|
* @param mode 校验模式(AND/OR)
|
||||||
|
* @param type 权限类型
|
||||||
|
* @param orRoles 或角色数组
|
||||||
|
*/
|
||||||
|
public void addPermission(String[] values, String mode, String type, String[] orRoles) {
|
||||||
|
if (values != null && values.length > 0) {
|
||||||
|
AuthInfo authInfo = new AuthInfo();
|
||||||
|
authInfo.setValues(values);
|
||||||
|
authInfo.setMode(mode);
|
||||||
|
authInfo.setType(type);
|
||||||
|
if (orRoles != null && orRoles.length > 0) {
|
||||||
|
authInfo.setOrValues(orRoles);
|
||||||
|
authInfo.setOrType("role");
|
||||||
|
}
|
||||||
|
this.permissions.add(authInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加角色信息
|
||||||
|
*
|
||||||
|
* @param values 角色值数组
|
||||||
|
* @param mode 校验模式(AND/OR)
|
||||||
|
* @param type 角色类型
|
||||||
|
*/
|
||||||
|
public void addRole(String[] values, String mode, String type) {
|
||||||
|
if (values != null && values.length > 0) {
|
||||||
|
AuthInfo authInfo = new AuthInfo();
|
||||||
|
authInfo.setValues(values);
|
||||||
|
authInfo.setMode(mode);
|
||||||
|
authInfo.setType(type);
|
||||||
|
this.roles.add(authInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 Markdown 结构的权限说明
|
||||||
|
*
|
||||||
|
* @return Markdown 文本
|
||||||
|
*/
|
||||||
|
public String toMarkdownString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("<br><h3>访问权限</h3><br>");
|
||||||
|
|
||||||
|
if (ignore) {
|
||||||
|
sb.append("> **权限策略**:忽略权限检查<br>");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ignore && permissions.isEmpty() && roles.isEmpty()){
|
||||||
|
sb.append("> **权限策略**:需要登录<br><br>");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!permissions.isEmpty()) {
|
||||||
|
sb.append("**权限校验:**<br><br>");
|
||||||
|
|
||||||
|
permissions.forEach(p -> {
|
||||||
|
String permTags = Arrays.stream(p.getValues())
|
||||||
|
.map(v -> "`" + v + "`")
|
||||||
|
.collect(Collectors.joining(p.getModeSymbol()));
|
||||||
|
|
||||||
|
sb.append("- ").append(permTags).append("<br>");
|
||||||
|
|
||||||
|
if (p.getOrValues() != null && p.getOrValues().length > 0) {
|
||||||
|
String orTags = Arrays.stream(p.getOrValues())
|
||||||
|
.map(v -> "`" + v + "`")
|
||||||
|
.collect(Collectors.joining(p.getModeSymbol()));
|
||||||
|
sb.append(" - 或角色:").append(orTags).append("<br>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.append("<br>");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!roles.isEmpty()) {
|
||||||
|
sb.append("**角色校验:**<br><br>");
|
||||||
|
|
||||||
|
roles.forEach(r -> {
|
||||||
|
|
||||||
|
String roleTags = Arrays.stream(r.getValues())
|
||||||
|
.map(v -> "`" + v + "`")
|
||||||
|
.collect(Collectors.joining(r.getModeSymbol()));
|
||||||
|
|
||||||
|
sb.append("- ").append(roleTags).append("<br>");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@JsonInclude(Include.NON_EMPTY)
|
||||||
|
public static class AuthInfo {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限或角色值数组
|
||||||
|
*/
|
||||||
|
private String[] values;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验模式(AND/OR)
|
||||||
|
*/
|
||||||
|
private String mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型说明
|
||||||
|
*/
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 或权限/角色值数组(用于权限校验时的或角色校验)
|
||||||
|
*/
|
||||||
|
private String[] orValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 或值的类型(role/permission)
|
||||||
|
*/
|
||||||
|
private String orType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重写mode的获取方法,返回符号而非文字
|
||||||
|
* @return AND→&,OR→|,默认→&
|
||||||
|
*/
|
||||||
|
public String getModeSymbol() {
|
||||||
|
if (mode == null) {
|
||||||
|
return " & "; // 默认AND,返回&
|
||||||
|
}
|
||||||
|
return "AND".equalsIgnoreCase(mode) ? " & " : " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.AnnotationUtil;
|
||||||
|
import cn.hutool.core.util.ClassLoaderUtil;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.AnnotatedElement;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抽象元数据 Javadoc 解析器
|
||||||
|
*
|
||||||
|
* @param <M> 元数据类型
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
public abstract class AbstractMetadataJavadocResolver<M> implements JavadocResolver {
|
||||||
|
|
||||||
|
public static final int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
|
||||||
|
public static final int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
private final Supplier<M> metadataProvider;
|
||||||
|
|
||||||
|
private final int order;
|
||||||
|
|
||||||
|
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider) {
|
||||||
|
this(metadataProvider, LOWEST_PRECEDENCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractMetadataJavadocResolver(Supplier<M> metadataProvider, int order) {
|
||||||
|
this.metadataProvider = metadataProvider;
|
||||||
|
this.order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolve(HandlerMethod handlerMethod, Operation operation) {
|
||||||
|
return resolve(handlerMethod, operation, metadataProvider.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行解析并返回解析到的 Javadoc 内容
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param operation Swagger Operation实例
|
||||||
|
* @param metadata 元信息
|
||||||
|
* @return 解析到的 Javadoc 内容
|
||||||
|
*/
|
||||||
|
public abstract String resolve(HandlerMethod handlerMethod, Operation operation, M metadata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法所属的类上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasClassAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法所属的类上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasClassAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getBeanType(), annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasMethodAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasMethodAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return AnnotationUtil.hasAnnotation(handlerMethod.getMethod(), annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasAnnotation(HandlerMethod handlerMethod,Class<? extends Annotation> annotationClass){
|
||||||
|
return this.hasClassAnnotation(handlerMethod, annotationClass) || this.hasMethodAnnotation(handlerMethod, annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查处理器方法上是否存在注解
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationTypeName 注解类名称
|
||||||
|
* @return 是否存在注解
|
||||||
|
*/
|
||||||
|
public boolean hasAnnotation(HandlerMethod handlerMethod, String annotationTypeName){
|
||||||
|
return this.hasClassAnnotation(handlerMethod, annotationTypeName) || this.hasMethodAnnotation(handlerMethod, annotationTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClassName 注解类名称
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> getClassAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
|
||||||
|
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getBeanType(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClass 注解类
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取处理器方法所属类上的注解的值
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param annotationClassName 注解类名称
|
||||||
|
* @return 注解的值
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public Map<String, Object> getMethodAnnotationValueMap(HandlerMethod handlerMethod, String annotationClassName) {
|
||||||
|
Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(annotationClassName, false);
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(handlerMethod.getMethod(), annotationClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getAnnotationValueMap(AnnotatedElement annotatedElement, Class<? extends Annotation> annotationClass) {
|
||||||
|
return AnnotationUtil.getAnnotationValueMap(annotatedElement, annotationClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javadoc解析器接口
|
||||||
|
*
|
||||||
|
* @author echo
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查解析器是否支持解析 HandlerMethod
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @return 是否支持解析
|
||||||
|
*/
|
||||||
|
boolean supports(HandlerMethod handlerMethod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行解析并返回解析到的 Javadoc 内容
|
||||||
|
* @param handlerMethod 处理器方法
|
||||||
|
* @param operation Swagger Operation实例
|
||||||
|
* @return 解析到的 Javadoc 内容
|
||||||
|
*/
|
||||||
|
String resolve(HandlerMethod handlerMethod, Operation operation);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取解析器优先级
|
||||||
|
*/
|
||||||
|
default int getOrder() {
|
||||||
|
return Ordered.LOWEST_PRECEDENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取解析器的名称
|
||||||
|
*
|
||||||
|
* @return 解析器名称
|
||||||
|
*/
|
||||||
|
default String getName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default int compareTo(@NotNull JavadocResolver o) {
|
||||||
|
return Integer.compare(getOrder(), o.getOrder());
|
||||||
|
}
|
||||||
|
}
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
package org.dromara.common.doc.core.resolver;
|
||||||
|
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ClassLoaderUtil;
|
||||||
|
import io.swagger.v3.oas.models.Operation;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.doc.core.model.SaTokenSecurityMetadata;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于JavaDoc的SaToken权限解析器
|
||||||
|
*
|
||||||
|
* @author echo
|
||||||
|
* @author 秋辞未寒
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Slf4j
|
||||||
|
public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJavadocResolver<SaTokenSecurityMetadata> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认元数据提供者,每次解析都会创建一个新的元数据对象
|
||||||
|
*/
|
||||||
|
public static final Supplier<SaTokenSecurityMetadata> DEFAULT_METADATA_PROVIDER = SaTokenSecurityMetadata::new;
|
||||||
|
|
||||||
|
private static final String BASE_CLASS_NAME = "cn.dev33.satoken.annotation";
|
||||||
|
private static final String SA_CHECK_ROLE_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckRole";
|
||||||
|
private static final String SA_CHECK_PERMISSION_CLASS_NAME = BASE_CLASS_NAME + ".SaCheckPermission";
|
||||||
|
private static final String SA_IGNORE_CLASS_NAME = BASE_CLASS_NAME + ".SaIgnore";
|
||||||
|
private static final String SA_CHECK_LOGIN_NAME = BASE_CLASS_NAME + ".SaCheckLogin";
|
||||||
|
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_ROLE_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_PERMISSION_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_IGNORE_CLASS;
|
||||||
|
private static final Class<? extends Annotation> SA_CHECK_LOGIN_CLASS;
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
// 通过类加载器去加载注解类Class实例
|
||||||
|
SA_CHECK_ROLE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_ROLE_CLASS_NAME, false);
|
||||||
|
SA_CHECK_PERMISSION_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_PERMISSION_CLASS_NAME, false);
|
||||||
|
SA_IGNORE_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_IGNORE_CLASS_NAME, false);
|
||||||
|
SA_CHECK_LOGIN_CLASS = (Class<? extends Annotation>) ClassLoaderUtil.loadClass(SA_CHECK_LOGIN_NAME, false);
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("SaTokenAnnotationJavadocResolver init success, load annotation class: {}", List.of(SA_CHECK_ROLE_CLASS, SA_CHECK_PERMISSION_CLASS, SA_IGNORE_CLASS, SA_CHECK_LOGIN_CLASS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver() {
|
||||||
|
this(DEFAULT_METADATA_PROVIDER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) {
|
||||||
|
super(metadataProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(int order) {
|
||||||
|
this(DEFAULT_METADATA_PROVIDER,order);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) {
|
||||||
|
super(metadataProvider,order);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(HandlerMethod handlerMethod) {
|
||||||
|
return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 检查是否忽略校验
|
||||||
|
if(hasAnnotation(handlerMethod, SA_IGNORE_CLASS_NAME)){
|
||||||
|
metadata.setIgnore(true);
|
||||||
|
return metadata.toMarkdownString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析权限校验
|
||||||
|
resolvePermissionCheck(handlerMethod, metadata);
|
||||||
|
|
||||||
|
// 解析角色校验
|
||||||
|
resolveRoleCheck(handlerMethod, metadata);
|
||||||
|
return metadata.toMarkdownString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析权限校验
|
||||||
|
*/
|
||||||
|
private void resolvePermissionCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 解析获取方法上的注解角色信息
|
||||||
|
if (hasMethodAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
|
||||||
|
resolvePermissionAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
// 解析获取类上的注解角色信息
|
||||||
|
if (hasClassAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_PERMISSION_CLASS);
|
||||||
|
resolvePermissionAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析权限注解
|
||||||
|
*/
|
||||||
|
private void resolvePermissionAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
|
||||||
|
try {
|
||||||
|
// 反射获取注解属性
|
||||||
|
Object value = annotationValueMap.get( "value");
|
||||||
|
Object mode = annotationValueMap.get( "mode");
|
||||||
|
Object type = annotationValueMap.get( "type");
|
||||||
|
Object orRole = annotationValueMap.get( "orRole");
|
||||||
|
|
||||||
|
String[] values = Convert.toStrArray(value);
|
||||||
|
String modeStr = mode != null ? mode.toString() : "AND";
|
||||||
|
String typeStr = type != null ? type.toString() : "";
|
||||||
|
String[] orRoles = Convert.toStrArray(orRole);
|
||||||
|
|
||||||
|
metadata.addPermission(values, modeStr, typeStr, orRoles);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 忽略解析错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析角色校验
|
||||||
|
*/
|
||||||
|
private void resolveRoleCheck(HandlerMethod handlerMethod, SaTokenSecurityMetadata metadata) {
|
||||||
|
// 解析获取方法上的注解角色信息
|
||||||
|
if (hasMethodAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getMethodAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
|
||||||
|
resolveRoleAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
// 解析获取类上的注解角色信息
|
||||||
|
if (hasClassAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS_NAME)) {
|
||||||
|
Map<String, Object> annotationValueMap = getClassAnnotationValueMap(handlerMethod, SA_CHECK_ROLE_CLASS);
|
||||||
|
resolveRoleAnnotation(metadata, annotationValueMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析角色注解
|
||||||
|
*/
|
||||||
|
private void resolveRoleAnnotation(SaTokenSecurityMetadata metadata, Map<String, Object> annotationValueMap) {
|
||||||
|
try {
|
||||||
|
// 反射获取注解属性
|
||||||
|
Object value = annotationValueMap.get("value");
|
||||||
|
Object mode = annotationValueMap.get("mode");
|
||||||
|
Object type = annotationValueMap.get("type");
|
||||||
|
|
||||||
|
String[] values = Convert.toStrArray(value);
|
||||||
|
String modeStr = mode != null ? mode.toString() : "AND";
|
||||||
|
String typeStr = type != null ? type.toString() : "";
|
||||||
|
|
||||||
|
metadata.addRole(values, modeStr, typeStr);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 忽略解析错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+25
-1
@@ -12,6 +12,7 @@ import io.swagger.v3.oas.models.tags.Tag;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.dromara.common.core.utils.StreamUtils;
|
import org.dromara.common.core.utils.StreamUtils;
|
||||||
|
import org.dromara.common.doc.core.resolver.JavadocResolver;
|
||||||
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
|
||||||
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
|
||||||
import org.springdoc.core.properties.SpringDocConfigProperties;
|
import org.springdoc.core.properties.SpringDocConfigProperties;
|
||||||
@@ -83,6 +84,11 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
*/
|
*/
|
||||||
private final PropertyResolverUtils propertyResolverUtils;
|
private final PropertyResolverUtils propertyResolverUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Javadoc解析器接口
|
||||||
|
*/
|
||||||
|
private final List<JavadocResolver> javadocResolvers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The javadoc provider.
|
* The javadoc provider.
|
||||||
*/
|
*/
|
||||||
@@ -123,7 +129,8 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
|
||||||
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
|
||||||
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
|
||||||
Optional<JavadocProvider> javadocProvider) {
|
Optional<JavadocProvider> javadocProvider,
|
||||||
|
List<JavadocResolver> javadocResolvers) {
|
||||||
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
|
||||||
if (openAPI.isPresent()) {
|
if (openAPI.isPresent()) {
|
||||||
this.openAPI = openAPI.get();
|
this.openAPI = openAPI.get();
|
||||||
@@ -140,6 +147,7 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
|
||||||
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
|
||||||
this.javadocProvider = javadocProvider;
|
this.javadocProvider = javadocProvider;
|
||||||
|
this.javadocResolvers = javadocResolvers == null ? new ArrayList<>() : javadocResolvers;
|
||||||
if (springDocConfigProperties.isUseFqn())
|
if (springDocConfigProperties.isUseFqn())
|
||||||
TypeNameResolver.std.setUseFqn(true);
|
TypeNameResolver.std.setUseFqn(true);
|
||||||
}
|
}
|
||||||
@@ -220,6 +228,22 @@ public class OpenApiHandler extends OpenAPIService {
|
|||||||
securityParser.buildSecurityRequirement(securityRequirements, operation);
|
securityParser.buildSecurityRequirement(securityRequirements, operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (javadocProvider.isPresent()) {
|
||||||
|
String description = javadocProvider.get().getMethodJavadocDescription(handlerMethod.getMethod());
|
||||||
|
String summary = javadocProvider.get().getFirstSentence(description);
|
||||||
|
if (StringUtils.isNotBlank(description)){
|
||||||
|
operation.setSummary(summary);
|
||||||
|
}
|
||||||
|
// 调用解析器提取JavaDoc中的权限信息
|
||||||
|
if (javadocResolvers != null && !javadocResolvers.isEmpty()) {
|
||||||
|
for (JavadocResolver resolver : javadocResolvers) {
|
||||||
|
String desc = resolver.resolve(handlerMethod, operation);
|
||||||
|
description = description + desc;
|
||||||
|
}
|
||||||
|
operation.setDescription(description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return operation;
|
return operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bouncycastle</groupId>
|
<groupId>org.bouncycastle</groupId>
|
||||||
<artifactId>bcprov-jdk15to18</artifactId>
|
<artifactId>bcpkix-jdk18on</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user