diff --git a/.gitignore b/.gitignore index 549e00a..d65ea6e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,7 @@ build/ ### VS Code ### .vscode/ + +### Custom ### +/application-config.yml +data diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c21afa2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM eclipse-temurin:17-jdk-alpine +LABEL authors="FatttSnake" + +VOLUME /data + +ARG EXTRACTED=target/extracted +COPY ${EXTRACTED}/dependencies/ / +COPY ${EXTRACTED}/spring-boot-loader/ / +COPY ${EXTRACTED}/snapshot-dependencies/ / +RUN true +COPY ${EXTRACTED}/application/ / + +ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher", "--spring.config.additional-location=file:data/"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f634ba --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +
+

+ Logo +
+ API of Oxygen Toolbox +

+
+ + +# Overview ([ZH](README_zh.md), EN) + +This project is the backend API of Oxygen Toolbox. Provides tool store, tool management, authentication, user management and other functions. + +# Requires + +- Java 17+ +- MySQL +- Redis + +# Related projects + +[Web UI of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-ui) + +[Desktop Client of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-desktop) + +[Android Client of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-android) + +# Quick Start + +1. First run, generate configuration file template + +```shell +java -jar oxygen-api.jar +``` + +2. Copy the `application-config.example.yml` file in the `data` directory to the running directory and rename it to `application-config.yml` + +```shell +cp ./data/application-config.example.yml application-config.yml +``` + +3. Edit the content of the configuration file `application-config.yml` + + +4. Run again + +```shell +java -jar oxygen-api.jar +``` + +# Security + +Integration with Spring Security and add other filter for jwt token process. The secret key is stored in `application-config.yml`. + +# Database + +Two databases, MySQL + SQLite, are used. MySQL is used to store key data, and SQLite is used to store logs and other data that require a large amount of reading and writing. + +# Q&A + +> **Q: What is the default administrator account and password?** +> +> A: If configured in `application-config.yml` before initializing the database, use the specified account and password. If not configured, a random password will be generated by default. See the console output for details. + +> **Q: Do I need to initialize the database?** +> +> A: This project uses `Flyway` to automatically initialize the database without manually defining the data table structure. To ensure data security, please back up the database before upgrading. diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..c6b2738 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,77 @@ +
+

+ Logo +
+ API of Oxygen Toolbox +

+
+ + +# 概述 (ZH, [EN](README.md)) + +本项目为 Oxygen Toolbox 的后端 API。提供工具商店、工具管理、认证鉴权、用户管理等功能。 + +# 环境要求 + +- Java 17+ +- MySQL +- Redis + +# 关联项目 + +[Web UI of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-ui) + +[Desktop Client of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-desktop) + +[Android Client of Oxygen Toolbox](https://github.com/FatttSnake/oxygen-android) + +# 快速开始 + +1. 初次运行,生成配置文件模板 + +```shell +java -jar oxygen-api.jar +``` + +2. 将 `data` 目录下的 `application-config.example.yml` 文件复制到运行目录下,并重命名为 `application-config.yml` + +```shell +cp ./data/application-config.example.yml application-config.yml +``` + +3. 编辑配置文件 `application-config.yml` 内容 + + +4. 再次运行 + +```shell +java -jar oxygen-api.jar +``` + +# 安全 + +集成 Spring Security 并采用 jwt 令牌, 密钥存储在 `application-config.yml` 中。 + +# 数据库 + +采用 MySQL + SQLite 双数据库,MySQL 用于存放关键数据,SQLite 用于存放日志等需要大量读写的数据。 + +# Q&A + +> **Q: 默认管理员账号和密码是什么?** +> +> A: 初始化数据库前配置在 `application-config.yml` 中,则使用所指定账号密码。未配置则默认生成随机密码,详见控制台输出。 + +> **Q: 是否需要初始化数据库?** +> +> A: 本项目采用 `Flyway` 自动初始化数据库,无需手动定义数据表结构。为保证数据安全,升级时请先备份数据库。 diff --git a/build-docker.sh b/build-docker.sh new file mode 100644 index 0000000..38a0c47 --- /dev/null +++ b/build-docker.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +JAR_NAME=`ls target | grep oxygen-api- | grep -vE "original|asc|pom|javadoc"` +JAR_VERSION=${JAR_NAME%.*} +JAR_VERSION=${JAR_VERSION#*-} +JAR_VERSION=${JAR_VERSION#*-} +BUILD_TIME=$(date "+%Y%m%d%H%M%S") + +echo ${BUILD_TIME} > .build_time + +mkdir target/extracted +java -Djarmode=layertools -jar target/${JAR_NAME} extract --destination target/extracted + +if [[ "${JAR_VERSION}" =~ ^.*SNAPSHOT$ ]] +then + docker build -t ${DOCKER_HUB_URL}/oxygen-api:snapshot-latest -t ${DOCKER_HUB_URL}/oxygen-api:${JAR_VERSION} -t ${DOCKER_HUB_URL}/oxygen-api:${JAR_VERSION}-${BUILD_TIME} . +else + docker build -t ${DOCKER_HUB_URL}/oxygen-api:latest -t ${DOCKER_HUB_URL}/oxygen-api:${JAR_VERSION} -t ${DOCKER_HUB_URL}/oxygen-api:${JAR_VERSION}-${BUILD_TIME} . +fi \ No newline at end of file diff --git a/doc/database.drawio b/doc/database.drawio new file mode 100644 index 0000000..1106267 --- /dev/null +++ b/doc/database.drawio @@ -0,0 +1,2073 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doc/logo.svg b/doc/logo.svg new file mode 100644 index 0000000..6b67342 --- /dev/null +++ b/doc/logo.svg @@ -0,0 +1 @@ + diff --git a/doc/permission.drawio b/doc/permission.drawio new file mode 100644 index 0000000..2ca4a6e --- /dev/null +++ b/doc/permission.drawio @@ -0,0 +1,520 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index ee5348b..f95846e 100644 --- a/pom.xml +++ b/pom.xml @@ -5,86 +5,96 @@ org.springframework.boot spring-boot-starter-parent - 3.1.4 + 3.2.1 - top.fatweb + + top.fatweb.oxygen api - 0.0.1-SNAPSHOT - fatweb-api - fatweb-api + 1.0.0 + + oxygen-api + API of Oxygen Toolbox + https://github.com/FatttSnake/oxygen-api + 17 - 1.8.22 + 1.9.21 + ${maven.build.timestamp} + yyyy-MM-dd'T'HH:mm:ss + 3.0.3 + 2.3 + 3.5.7 + 4.3.1 + 9.22.3 + 4.4.0 + 1.1.0 + 6.4.9 + 3.5.3 + 4.4.0 - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - com.fasterxml.jackson.module - jackson-module-kotlin - - - org.jetbrains.kotlin - kotlin-reflect - - - org.jetbrains.kotlin - kotlin-stdlib - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - + + FatWeb + https://fatweb.top + + + + + GNU General Public License v3.0 or later + https://www.gnu.org/licenses/gpl-3.0-standalone.html + repo + + + + + + fatttsnake + FatttSnake + fatttsnake@gmail.com + + + ${project.name}-${project.version} ${project.basedir}/src/main/kotlin ${project.basedir}/src/test/kotlin - - org.graalvm.buildtools - native-maven-plugin - org.springframework.boot spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - org.jetbrains.kotlin kotlin-maven-plugin + + + compile + compile + + compile + + + + src/main/kotlin + target/generated-sources/annotations + + + + + test-compile + test-compile + + test-compile + + + + src/main/kotlin + target/generated-sources/annotations + + + + -Xjsr305=strict @@ -101,7 +111,229 @@ + + maven-surefire-plugin + 2.22.2 + + + maven-failsafe-plugin + 3.1.2 + + + org.jetbrains.dokka + dokka-maven-plugin + 1.9.10 + + + pre-site + + dokka + + + + attach-javadocJar + package + + javadocJar + + + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + com.github.lianjiatech + retrofit-spring-boot-starter + ${retrofit.version} + + + + org.jetbrains.kotlin + kotlin-reflect + + + org.jetbrains.kotlin + kotlin-stdlib + + + com.fasterxml.jackson.module + jackson-module-kotlin + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + com.mysql + mysql-connector-j + runtime + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + org.xerial + sqlite-jdbc + + + com.baomidou + dynamic-datasource-spring-boot3-starter + ${dynamic-datasource.version} + + + org.flywaydb + flyway-mysql + ${flyway.version} + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.auth0 + java-jwt + ${jwt.version} + + + top.fatweb + avatar-generator + ${avatar-generator.version} + + + com.github.oshi + oshi-core + ${oshi.version} + + + commons-codec + commons-codec + + + com.google.zxing + core + ${zxing.version} + + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + com.baomidou + mybatis-plus-spring-boot3-starter-test + ${mybatis-plus.version} + test + + + + + + dev + + true + + env + dev + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + + + + + release + + + env + release + + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + + + com.github.xiaoymin + knife4j-openapi3-ui + + + org.webjars + swagger-ui + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.1.0 + + + sign-artifact + verify + + sign + + + + + + + + diff --git a/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt b/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt deleted file mode 100644 index f77dbd7..0000000 --- a/src/main/kotlin/top/fatweb/api/FatWebApiApplication.kt +++ /dev/null @@ -1,11 +0,0 @@ -package top.fatweb.api - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -@SpringBootApplication -class FatWebApiApplication - -fun main(args: Array) { - runApplication(*args) -} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/OxygenApiApplication.kt b/src/main/kotlin/top/fatweb/oxygen/api/OxygenApiApplication.kt new file mode 100644 index 0000000..6bfaadb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/OxygenApiApplication.kt @@ -0,0 +1,67 @@ +package top.fatweb.oxygen.api + +import org.slf4j.LoggerFactory +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.boot.runApplication +import org.springframework.scheduling.annotation.EnableScheduling +import org.springframework.transaction.annotation.EnableTransactionManagement +import java.io.File +import java.util.* + +/** + * Application main class + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@SpringBootApplication +@EnableTransactionManagement +@EnableScheduling +class OxygenApiApplication + +/** + * Main function + * + * @param args + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +fun main(args: Array) { + val logger = LoggerFactory.getLogger("main") + + if (!File("data").isDirectory) { + if (!File("data").mkdir()) { + logger.error("Can not create directory 'data', please try again later.") + return + } + } + + if (!File("data/db").isDirectory) { + if (!File("data/db").mkdir()) { + logger.error("Can not create directory 'data/db', please try again later.") + return + } + } + + if (!File("data/db/sqlite.db").isFile || File("data/db/sqlite.db").inputStream() + .use { it.readNBytes(15).toString(Charsets.UTF_8) != "SQLite format 3" } + ) { + logger.warn("The 'data/db/sqlite.db' database is lost or damaged, recreating...") + if (File("data/db/sqlite.db").exists() && !File("data/db/sqlite.db").delete()) { + logger.error("Can not recreate database 'data/db/sqlite.db', please try again later.") + } + } + + if (File("application-config.yml").exists() || File("data/application-config.yml").exists()) { + runApplication(*args) + } else { + logger.warn("File 'application-config.yml' cannot be found in the running path or the data path. The configuration file template 'application-config.example.yml' has been created in directory 'data'. Please change the configuration file content, move it to the running path, rename it to 'application-config.yml', and then restart the server.") + OxygenApiApplication::class.java.getResource("/application-config-template.yml")?.readText()?.let { + File("data/application-config.example.yml").writeText( + it.replace( + "\$uuid\$", UUID.randomUUID().toString() + ) + ) + } + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/annotation/ApiController.kt b/src/main/kotlin/top/fatweb/oxygen/api/annotation/ApiController.kt new file mode 100644 index 0000000..5805c4e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/annotation/ApiController.kt @@ -0,0 +1,32 @@ +package top.fatweb.oxygen.api.annotation + +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.core.annotation.AliasFor +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +/** + * API controller annotation + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tag + * @see RequestMapping + * @see RestController + */ +@Tag(name = "") +@RequestMapping +@RestController +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class ApiController( + val version: Int = 1, + + @get:AliasFor(annotation = RestController::class, attribute = "value") val value: String = "", + + @get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array = [""], + + @get:AliasFor(annotation = Tag::class, attribute = "name") val name: String, + + @get:AliasFor(annotation = Tag::class, attribute = "description") val description: String +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/annotation/BaseController.kt b/src/main/kotlin/top/fatweb/oxygen/api/annotation/BaseController.kt new file mode 100644 index 0000000..598555d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/annotation/BaseController.kt @@ -0,0 +1,27 @@ +package top.fatweb.oxygen.api.annotation + +import io.swagger.v3.oas.annotations.tags.Tag +import org.springframework.core.annotation.AliasFor +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +/** + * Base controller annotation + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RequestMapping + * @see RestController + */ +@Tag(name = "") +@RequestMapping +@RestController +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class BaseController( + @get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array = [""], + + @get:AliasFor(annotation = Tag::class, attribute = "name") val name: String, + + @get:AliasFor(annotation = Tag::class, attribute = "description") val description: String +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/annotation/EventLogRecord.kt b/src/main/kotlin/top/fatweb/oxygen/api/annotation/EventLogRecord.kt new file mode 100644 index 0000000..f0e023b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/annotation/EventLogRecord.kt @@ -0,0 +1,15 @@ +package top.fatweb.oxygen.api.annotation + +import top.fatweb.oxygen.api.entity.system.EventLog + +/** + * Event log record annotation + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class EventLogRecord( + val event: EventLog.Event +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/annotation/HiddenController.kt b/src/main/kotlin/top/fatweb/oxygen/api/annotation/HiddenController.kt new file mode 100644 index 0000000..90287fb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/annotation/HiddenController.kt @@ -0,0 +1,24 @@ +package top.fatweb.oxygen.api.annotation + +import io.swagger.v3.oas.annotations.Hidden +import org.springframework.core.annotation.AliasFor +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +/** + * Hidden controller annotation + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Hidden + * @see RequestMapping + * @see RestController + */ +@Hidden +@RequestMapping +@RestController +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class HiddenController( + @get:AliasFor(annotation = RequestMapping::class, attribute = "path") val path: Array = [""] +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/annotation/Trim.kt b/src/main/kotlin/top/fatweb/oxygen/api/annotation/Trim.kt new file mode 100644 index 0000000..f3bdc81 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/annotation/Trim.kt @@ -0,0 +1,15 @@ +package top.fatweb.oxygen.api.annotation + +import java.lang.annotation.Inherited + + +/** + * Trim string annotation + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.RUNTIME) +@Inherited +annotation class Trim \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/aop/EventLogInterceptor.kt b/src/main/kotlin/top/fatweb/oxygen/api/aop/EventLogInterceptor.kt new file mode 100644 index 0000000..5a61beb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/aop/EventLogInterceptor.kt @@ -0,0 +1,58 @@ +package top.fatweb.oxygen.api.aop + +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.AfterReturning +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Pointcut +import org.aspectj.lang.reflect.MethodSignature +import org.springframework.stereotype.Component +import top.fatweb.oxygen.api.annotation.EventLogRecord +import top.fatweb.oxygen.api.service.system.IEventLogService +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.permission.LoginVo +import top.fatweb.oxygen.api.vo.permission.RegisterVo + +/** + * Event log record interceptor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IEventLogService + */ +@Aspect +@Component +class EventLogInterceptor( + private val eventLogService: IEventLogService +) { + /** + * Event log record pointcut + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Pointcut("@annotation(top.fatweb.oxygen.api.annotation.EventLogRecord)") + fun eventLogPointcut() { + } + + /** + * Do after event log record pointcut + * + * @param joinPoint Join point + * @param retValue Return value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see JoinPoint + */ + @AfterReturning(value = "eventLogPointcut()", returning = "retValue") + fun doAfter(joinPoint: JoinPoint, retValue: Any?) { + val annotation = (joinPoint.signature as MethodSignature).method.getAnnotation(EventLogRecord::class.java) + + val userId = WebUtil.getLoginUserId() ?: when (retValue) { + is LoginVo -> retValue.userId!! + is RegisterVo -> retValue.userId!! + else -> -1 + } + + eventLogService.saveEvent(annotation, userId) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/aop/SysLogInterceptor.kt b/src/main/kotlin/top/fatweb/oxygen/api/aop/SysLogInterceptor.kt new file mode 100644 index 0000000..5c8a27c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/aop/SysLogInterceptor.kt @@ -0,0 +1,136 @@ +package top.fatweb.oxygen.api.aop + +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.core.MethodParameter +import org.springframework.http.MediaType +import org.springframework.http.converter.HttpMessageConverter +import org.springframework.http.server.ServerHttpRequest +import org.springframework.http.server.ServerHttpResponse +import org.springframework.web.bind.annotation.ControllerAdvice +import org.springframework.web.servlet.HandlerInterceptor +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.entity.system.SysLog +import top.fatweb.oxygen.api.service.system.ISysLogService +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.permission.LoginVo +import java.net.URI +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.temporal.ChronoUnit +import java.util.* +import java.util.concurrent.Executor + +/** + * System log interceptor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Executor + * @see ISysLogService + * @see HandlerInterceptor + * @see ResponseBodyAdvice + */ +@ControllerAdvice +class SysLogInterceptor( + @Qualifier("applicationTaskExecutor") private val customThreadPoolTaskExecutor: Executor, + private val sysLogService: ISysLogService +) : HandlerInterceptor, ResponseBodyAdvice { + private val sysLogThreadLocal = ThreadLocal() + private val resultThreadLocal = ThreadLocal() + + override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean { + val sysLog = SysLog().apply { + operateUserId = WebUtil.getLoginUserId() ?: -1 + startTime = LocalDateTime.now(ZoneOffset.UTC) + requestUri = URI(request.requestURI).path + requestParams = formatParams(request.parameterMap) + requestMethod = request.method + requestIp = WebUtil.getRequestIp(request) + requestServerAddress = "${request.scheme}://${request.serverName}:${request.serverPort}" + userAgent = request.getHeader("User-Agent") + } + + sysLogThreadLocal.set(sysLog) + + return true + } + + override fun afterCompletion( + request: HttpServletRequest, response: HttpServletResponse, handler: Any, ex: Exception? + ) { + val sysLog = sysLogThreadLocal.get() + val result = resultThreadLocal.get() + sysLog.endTime = LocalDateTime.now(ZoneOffset.UTC) + sysLog.executeTime = ChronoUnit.MILLIS.between(sysLog.startTime, sysLog.endTime) + if (result is ResponseResult<*>) { + if (result.success) { + sysLog.apply { + logType = requestUri?.let { + when { + it.startsWith("/login") -> SysLog.LogType.LOGIN + it.startsWith("/logout") -> SysLog.LogType.LOGOUT + it.startsWith("/register") -> SysLog.LogType.REGISTER + it.startsWith("/system/statistics/") -> SysLog.LogType.STATISTICS + it.startsWith("/api/") -> SysLog.LogType.API + else -> SysLog.LogType.INFO + } + } ?: SysLog.LogType.INFO + exception = 0 + } + if (result.data is LoginVo) { + sysLog.operateUserId = result.data.userId ?: -1 + } + } else { + sysLog.apply { + logType = SysLog.LogType.ERROR + exception = 1 + exceptionInfo = result.msg + } + } + + customThreadPoolTaskExecutor.execute(SaveLogThread(sysLog, sysLogService)) + } + sysLogThreadLocal.remove() + } + + private fun formatParams(parameterMap: Map>): String { + val params = StringJoiner("&") + + parameterMap.forEach { + params.add("${it.key}=${if (it.key.endsWith("password", true)) "*" else it.value.joinToString(",")}") + } + + return params.toString() + } + + private class SaveLogThread(val sysLog: SysLog, val sysLogService: ISysLogService) : Thread() { + override fun run() { + sysLog.operateTime = LocalDateTime.now(ZoneOffset.UTC) + sysLogService.save(sysLog) + } + } + + override fun supports(returnType: MethodParameter, converterType: Class>): Boolean = + true + + override fun beforeBodyWrite( + body: Any?, + returnType: MethodParameter, + selectedContentType: MediaType, + selectedConverterType: Class>, + request: ServerHttpRequest, + response: ServerHttpResponse + ): Any? { + resultThreadLocal.set(body) + + if (body is ResponseResult<*> && body.code == ResponseCode.SYSTEM_ERROR.code) { + return ResponseResult.build(body.code, body.success, "fail", body.data) + } + + return body + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/aop/TrimInterceptor.kt b/src/main/kotlin/top/fatweb/oxygen/api/aop/TrimInterceptor.kt new file mode 100644 index 0000000..fae937f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/aop/TrimInterceptor.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.aop + +import org.aspectj.lang.JoinPoint +import org.aspectj.lang.annotation.Aspect +import org.aspectj.lang.annotation.Before +import org.aspectj.lang.annotation.Pointcut +import org.springframework.stereotype.Component +import org.springframework.web.servlet.HandlerInterceptor +import top.fatweb.oxygen.api.annotation.Trim +import kotlin.reflect.KMutableProperty +import kotlin.reflect.full.* +import kotlin.reflect.jvm.isAccessible + +/** + * Trim string interceptor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HandlerInterceptor + */ +@Component +@Aspect +class TrimInterceptor : HandlerInterceptor { + /** + * Trim pointcut + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Pointcut("@annotation(top.fatweb.oxygen.api.annotation.Trim)") + fun trimPointcut() { + } + + /** + * Do before trim pointcut + * + * @param joinPoint Join point + * @return Arguments + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see JoinPoint + */ + @Before("trimPointcut()") + fun doBefore(joinPoint: JoinPoint): Any { + val args = joinPoint.args + + args?.forEachIndexed { index, any -> + if (args[index]::class.hasAnnotation()) args[index] = trim(any) + } + + return args + } + + private fun trim(any: Any?): Any? { + any ?: return null + + when (any) { + is Boolean, Short, Int, Long, Float, Double -> { + return any + } + + is String -> { + return any.trim() + } + + else -> { + val members = any::class.declaredMemberProperties + if (members.isEmpty()) { + return any + } + members.forEach { + if (!it.returnType.isSupertypeOf(String::class.starProjectedType) + || it !is KMutableProperty<*> + || !it.hasAnnotation() + ) { + return@forEach + } + it.isAccessible = true + if (it.call(any) != null) { + it.setter.call(any, (it.call(any) as String).trim()) + } + } + return any + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/DateFormatConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/DateFormatConfig.kt new file mode 100644 index 0000000..a2ff193 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/DateFormatConfig.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.config + +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer +import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer +import org.springframework.boot.jackson.JsonComponent +import org.springframework.context.annotation.Bean +import java.text.SimpleDateFormat +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.* + +/** + * Date format configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@JsonComponent +class DateFormatConfig { + /** + * The format of the time in response when request APIs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @set:Value("\${spring.jackson.date-format}") + lateinit var dateFormat: String + + /** + * The timezone of the time in response when request APIs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TimeZone + */ + @set:Value("\${spring.jackson.time-zone}}") + lateinit var timeZone: TimeZone + + @Bean + fun jackson2ObjectMapperBuilder() = Jackson2ObjectMapperBuilderCustomizer { + val dateFormat = SimpleDateFormat(dateFormat) + dateFormat.timeZone = timeZone + it.failOnEmptyBeans(false).failOnUnknownProperties(false) + .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).dateFormat(dateFormat) + } + + @Bean + fun jackson2ObjectMapperBuilderCustomizer() = + Jackson2ObjectMapperBuilderCustomizer { + it.serializerByType( + LocalDateTime::class.java, LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateFormat)) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/FilterConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/FilterConfig.kt new file mode 100644 index 0000000..54d659e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/FilterConfig.kt @@ -0,0 +1,22 @@ +package top.fatweb.oxygen.api.config + +import org.springframework.boot.web.servlet.FilterRegistrationBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import top.fatweb.oxygen.api.filter.ExceptionFilter + +/** + * Filter configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class FilterConfig { + @Bean + fun exceptionFilterRegistrationBean(exceptionFilter: ExceptionFilter): FilterRegistrationBean = + FilterRegistrationBean(exceptionFilter).apply { + setBeanName("exceptionFilter") + order = -100 + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/FlywayConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/FlywayConfig.kt new file mode 100644 index 0000000..9e34385 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/FlywayConfig.kt @@ -0,0 +1,43 @@ +package top.fatweb.oxygen.api.config + +import com.baomidou.dynamic.datasource.DynamicRoutingDataSource +import jakarta.annotation.PostConstruct +import org.flywaydb.core.Flyway +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.DependsOn +import top.fatweb.oxygen.api.properties.FlywayProperties +import javax.sql.DataSource + +/** + * Flyway configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@DependsOn("flywayProperties") +@Configuration +class FlywayConfig( + private val dataSource: DataSource +) { + @PostConstruct + fun migrateOrder() { + val ds = dataSource as DynamicRoutingDataSource + ds.dataSources.forEach { (k: String, v: DataSource?) -> + val flyway = Flyway.configure() + .dataSource(v) + .locations(*FlywayProperties.locations.map { "$it/$k" }.toTypedArray()) + .baselineOnMigrate(FlywayProperties.baselineOnMigrate) + .table(FlywayProperties.table) + .outOfOrder(FlywayProperties.outOfOrder) + .validateOnMigrate(FlywayProperties.validateOnMigrate) + .encoding(FlywayProperties.encoding) + .sqlMigrationPrefix(FlywayProperties.sqlMigrationPrefix) + .sqlMigrationSeparator(FlywayProperties.sqlMigrationSeparator) + .sqlMigrationSuffixes(*FlywayProperties.sqlMigrationSuffixes.toTypedArray()) + .baselineVersion(FlywayProperties.baselineVersion) + .load() + flyway.migrate() + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/InitConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/InitConfig.kt new file mode 100644 index 0000000..7e4d123 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/InitConfig.kt @@ -0,0 +1,69 @@ +package top.fatweb.oxygen.api.config + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import jakarta.annotation.PostConstruct +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.context.annotation.DependsOn +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Component +import top.fatweb.avatargenerator.GitHubAvatar +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.properties.AdminProperties +import top.fatweb.oxygen.api.service.permission.IUserInfoService +import top.fatweb.oxygen.api.service.permission.IUserService +import top.fatweb.oxygen.api.util.StrUtil + +/** + * Application initialization configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IUserService + * @see IUserInfoService + * @see PasswordEncoder + */ +@DependsOn("adminProperties") +@Component +class InitConfig( + private val userService: IUserService, + private val userInfoService: IUserInfoService, + private val passwordEncoder: PasswordEncoder +) { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + @PostConstruct + fun init() { + if (!userService.exists(KtQueryWrapper(User()).eq(User::id, 0))) { + userInfoService.remove(KtQueryWrapper(UserInfo()).eq(UserInfo::userId, 0)) + + val rawPassword = AdminProperties.password ?: let { + logger.warn("No default administrator password is set, a randomly generated password will be used") + StrUtil.getRandomPassword(10) + } + val encodedPassword = passwordEncoder.encode(rawPassword) + + val user = User().apply { + id = 0 + username = AdminProperties.username + password = encodedPassword + locking = 0 + enable = 1 + } + val userInfo = UserInfo().apply { + userId = 0 + nickname = AdminProperties.nickname + avatar = + GitHubAvatar.newAvatarBuilder().build().createAsBase64((Long.MIN_VALUE..Long.MAX_VALUE).random()) + email = AdminProperties.email + } + + if (userService.save(user) && userInfoService.save(userInfo)) { + logger.warn("First startup, create administrator - username: admin, password: $rawPassword") + logger.warn("This information will only be shown once. Please change your password promptly after logging in.") + } + } + + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/JacksonConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/JacksonConfig.kt new file mode 100644 index 0000000..795435d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/JacksonConfig.kt @@ -0,0 +1,27 @@ +package top.fatweb.oxygen.api.config + +import com.fasterxml.jackson.annotation.JsonInclude +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.json.JsonMapper +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import retrofit2.converter.jackson.JacksonConverterFactory + +/** + * Jackson configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class JacksonConfig { + @Bean + fun jacksonConverterFactory(): JacksonConverterFactory = + JacksonConverterFactory.create( + JsonMapper.builder() + .findAndAddModules() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .build() + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/MybatisPlusConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/MybatisPlusConfig.kt new file mode 100644 index 0000000..0d163bd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/MybatisPlusConfig.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.config + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * Mybatis-plus configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class MybatisPlusConfig { + @Bean + fun mybatisPlusInterceptor(): MybatisPlusInterceptor = + MybatisPlusInterceptor().apply { + addInnerInterceptor(OptimisticLockerInnerInterceptor()) + addInnerInterceptor(PaginationInnerInterceptor()) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/RedisConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/RedisConfig.kt new file mode 100644 index 0000000..a28eacb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/RedisConfig.kt @@ -0,0 +1,48 @@ +package top.fatweb.oxygen.api.config + +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.PropertyAccessor +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.connection.RedisConnectionFactory +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer +import org.springframework.data.redis.serializer.StringRedisSerializer + +/** + * Redis configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class RedisConfig { + @Bean + fun redisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<*, *> { + val redisTemplate = RedisTemplate() + redisTemplate.connectionFactory = redisConnectionFactory + val stringRedisSerializer = StringRedisSerializer() + val objectMapper = ObjectMapper().registerModules(JavaTimeModule()).apply { + setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY) + activateDefaultTyping( + this.polymorphicTypeValidator, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY + ) + } + val anyJackson2JsonRedisSerializer = Jackson2JsonRedisSerializer(objectMapper, Any::class.java) + + // Use String Redis Serializer to serialize and deserialize redis key values + redisTemplate.keySerializer = stringRedisSerializer + redisTemplate.valueSerializer = anyJackson2JsonRedisSerializer + + // The Hash key also uses the String Redis Serializer serialization method. + redisTemplate.hashKeySerializer = stringRedisSerializer + redisTemplate.hashValueSerializer = anyJackson2JsonRedisSerializer + + redisTemplate.afterPropertiesSet() + + return redisTemplate + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/SecurityConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/SecurityConfig.kt new file mode 100644 index 0000000..c62c302 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/SecurityConfig.kt @@ -0,0 +1,108 @@ +package top.fatweb.oxygen.api.config + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity +import org.springframework.security.config.annotation.web.builders.HttpSecurity +import org.springframework.security.config.http.SessionCreationPolicy +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder +import org.springframework.security.web.SecurityFilterChain +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter +import org.springframework.web.cors.CorsConfiguration +import org.springframework.web.cors.UrlBasedCorsConfigurationSource +import top.fatweb.oxygen.api.filter.JwtAuthenticationTokenFilter +import top.fatweb.oxygen.api.handler.JwtAccessDeniedHandler +import top.fatweb.oxygen.api.handler.JwtAuthenticationEntryPointHandler + +/** + * Spring Security configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see JwtAuthenticationTokenFilter + * @see JwtAuthenticationEntryPointHandler + * @see JwtAccessDeniedHandler + */ +@Configuration +@EnableMethodSecurity +class SecurityConfig( + private val jwtAuthenticationTokenFilter: JwtAuthenticationTokenFilter, + private val authenticationEntryPointHandler: JwtAuthenticationEntryPointHandler, + private val accessDeniedHandler: JwtAccessDeniedHandler +) { + @Bean + fun passwordEncoder() = BCryptPasswordEncoder() + + @Bean + fun authenticationManager(authenticationConfiguration: AuthenticationConfiguration): AuthenticationManager = + authenticationConfiguration.authenticationManager + + @Bean + fun corsConfigurationSource(): UrlBasedCorsConfigurationSource { + val corsConfiguration = CorsConfiguration() + corsConfiguration.allowedMethods = listOf("*") + corsConfiguration.allowedHeaders = listOf("*") + corsConfiguration.maxAge = 3600L + corsConfiguration.allowedOrigins = listOf("*") + val source = UrlBasedCorsConfigurationSource() + source.registerCorsConfiguration("/**", corsConfiguration) + + return source + } + + @Bean + fun securityFilterChain(httpSecurity: HttpSecurity): SecurityFilterChain = httpSecurity + // Disable CSRF + .csrf { + it.disable() + } + // Do not get SecurityContent by Session + .sessionManagement { + it.sessionCreationPolicy( + SessionCreationPolicy.STATELESS + ) + } + .authorizeHttpRequests { + it + // Allow anonymous access + .requestMatchers( + "/error/thrown", + "/doc.html", + "/swagger-ui/**", + "/webjars/**", + "/v3/**", + "/swagger-ui.html", + "/favicon.ico", + "/login", + "/register", + "/forget", + "/retrieve" + ).anonymous() + .requestMatchers("/tool/detail/**", "/tool/store", "/tool/store/*", "/system/user/info/*").permitAll() + // Authentication required + .anyRequest().authenticated() + } + + .logout { + it.disable() + } + + .exceptionHandling { + it.authenticationEntryPoint( + authenticationEntryPointHandler + ) + it.accessDeniedHandler( + accessDeniedHandler + ) + } + + .cors { + it.configurationSource( + corsConfigurationSource() + ) + } + + .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter::class.java).build() +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/SwaggerConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/SwaggerConfig.kt new file mode 100644 index 0000000..af0ca61 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/SwaggerConfig.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.api.config + +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.info.Contact +import io.swagger.v3.oas.models.info.Info +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import top.fatweb.oxygen.api.properties.ServerProperties + +/** + * Swagger API doc configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class SwaggerConfig { + @Bean + fun customOpenAPI(): OpenAPI? { + val contact = Contact().name("FatttSnake").url("https://fatweb.top").email("fatttsnake@gmail.com") + return OpenAPI().info( + Info().title("Oxygen API 文档").description("Oxygen 后端 API 文档,包含各个 Controller 调用信息") + .contact(contact).version( + ServerProperties.version + ) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/SysLogConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/SysLogConfig.kt new file mode 100644 index 0000000..f7a29b8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/SysLogConfig.kt @@ -0,0 +1,24 @@ +package top.fatweb.oxygen.api.config + +import org.springframework.context.annotation.Configuration +import org.springframework.web.servlet.config.annotation.InterceptorRegistry +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer +import top.fatweb.oxygen.api.aop.SysLogInterceptor + +/** + * System log configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SysLogInterceptor + * @see WebMvcConfigurer + */ +@Configuration +class SysLogConfig( + private val sysLogInterceptor: SysLogInterceptor +) : WebMvcConfigurer { + override fun addInterceptors(registry: InterceptorRegistry) { + registry.addInterceptor(sysLogInterceptor).addPathPatterns("/**") + .excludePathPatterns("/error/thrown", "/webjars/**") + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/VelocityEngineConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/VelocityEngineConfig.kt new file mode 100644 index 0000000..0995ada --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/VelocityEngineConfig.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.config + +import org.apache.velocity.app.VelocityEngine +import org.apache.velocity.runtime.RuntimeConstants +import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +/** + * Velocity engine configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Configuration +class VelocityEngineConfig { + @Bean + fun velocityEngine() = VelocityEngine().apply { + setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath") + setProperty("classpath.resource.loader.class", ClasspathResourceLoader::class.java.name) + init() + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcConfigurerConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcConfigurerConfig.kt new file mode 100644 index 0000000..604a8ab --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcConfigurerConfig.kt @@ -0,0 +1,21 @@ +package top.fatweb.oxygen.api.config + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations +import org.springframework.context.annotation.Configuration +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer +import top.fatweb.oxygen.api.annotation.ApiController + +/** + * Web MVC configurer configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see WebMvcRegistrations + */ +@Configuration +class WebMvcConfigurerConfig : WebMvcConfigurer { + override fun configurePathMatch(configurer: PathMatchConfigurer) { + configurer.addPathPrefix("/api/{API_VERSION}") { it.isAnnotationPresent(ApiController::class.java) } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcRegistrationsConfig.kt b/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcRegistrationsConfig.kt new file mode 100644 index 0000000..f65a30c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/config/WebMvcRegistrationsConfig.kt @@ -0,0 +1,18 @@ +package top.fatweb.oxygen.api.config + +import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations +import org.springframework.context.annotation.Configuration +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping +import top.fatweb.oxygen.api.util.ApiResponseMappingHandlerMapping + +/** + * Web MVC registrations configuration + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see WebMvcRegistrations + */ +@Configuration +class WebMvcRegistrationsConfig : WebMvcRegistrations { + override fun getRequestMappingHandlerMapping(): RequestMappingHandlerMapping = ApiResponseMappingHandlerMapping() +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/ExceptionController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/ExceptionController.kt new file mode 100644 index 0000000..c61cceb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/ExceptionController.kt @@ -0,0 +1,19 @@ +package top.fatweb.oxygen.api.controller + +import jakarta.servlet.http.HttpServletRequest +import org.springframework.web.bind.annotation.RequestMapping +import top.fatweb.oxygen.api.annotation.HiddenController + +/** + * Exception controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@HiddenController(["/error"]) +class ExceptionController { + @RequestMapping("/thrown") + fun thrown(request: HttpServletRequest) { + throw request.getAttribute("filter.error") as RuntimeException + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/api/v1/AvatarController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/api/v1/AvatarController.kt new file mode 100644 index 0000000..0747aff --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/api/v1/AvatarController.kt @@ -0,0 +1,204 @@ +package top.fatweb.oxygen.api.controller.api.v1 + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.http.MediaType +import org.springframework.web.bind.annotation.GetMapping +import top.fatweb.oxygen.api.annotation.ApiController +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarBaseParam +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarGitHubParam +import top.fatweb.oxygen.api.service.api.v1.IAvatarService +import top.fatweb.oxygen.api.vo.api.v1.avatar.AvatarBase64Vo + +/** + * Avatar controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IAvatarService + */ +@ApiController(value = "avatarControllerV1", path = ["/avatar"], name = "随机头像 V1", description = "随机头像相关接口") +class AvatarController( + private val avatarService: IAvatarService +) { + /** + * Get random avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + @Operation(summary = "获取随机头像") + @GetMapping(produces = [MediaType.IMAGE_PNG_VALUE]) + fun getRandom(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray = + avatarService.random(avatarBaseParam) + + /** + * Get random avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return Response object includes avatar base64 string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ResponseResult + * @see AvatarBase64Vo + */ + @Operation(summary = "获取随机头像 Base64") + @GetMapping("base64") + fun getRandomBase64( + @Valid avatarBaseParam: AvatarBaseParam? + ): ResponseResult = + ResponseResult.success( + ResponseCode.API_AVATAR_SUCCESS, data = avatarService.randomBase64(avatarBaseParam) + ) + + /** + * Get triangle avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + @Operation(summary = "三角形头像") + @GetMapping("/triangle", produces = [MediaType.IMAGE_PNG_VALUE]) + fun triangle(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray = + avatarService.triangle(avatarBaseParam) + + /** + * Get triangle avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return Response object includes avatar base64 string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ResponseResult + * @see AvatarBase64Vo + */ + @Operation(summary = "三角形头像 Base64") + @GetMapping("/triangle/base64") + fun triangleBase64( + @Valid avatarBaseParam: AvatarBaseParam? + ): ResponseResult = + ResponseResult.success( + ResponseCode.API_AVATAR_SUCCESS, + data = avatarService.triangleBase64(avatarBaseParam) + ) + + /** + * Get square avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + @Operation(summary = "正方形头像") + @GetMapping("/square", produces = [MediaType.IMAGE_PNG_VALUE]) + fun square(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray = + avatarService.square(avatarBaseParam) + + /** + * Get square avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return Response object includes avatar base64 string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ResponseResult + * @see AvatarBase64Vo + */ + @Operation(summary = "正方形头像 Base64") + @GetMapping("/square/base64") + fun squareBase64( + @Valid avatarBaseParam: AvatarBaseParam? + ): ResponseResult = + ResponseResult.success( + ResponseCode.API_AVATAR_SUCCESS, + data = avatarService.squareBase64(avatarBaseParam) + ) + + /** + * Get identicon avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + @Operation(summary = "Identicon 头像") + @GetMapping("/identicon", produces = [MediaType.IMAGE_PNG_VALUE]) + fun identicon(@Valid avatarBaseParam: AvatarBaseParam?): ByteArray = + avatarService.identicon(avatarBaseParam) + + /** + * Get identicon avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return Response object includes avatar base64 string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ResponseResult + * @see AvatarBase64Vo + */ + @Operation(summary = "Identicon 头像 Base64") + @GetMapping("/identicon/base64") + fun identiconBase64( + @Valid avatarBaseParam: AvatarBaseParam? + ): ResponseResult = + ResponseResult.success( + ResponseCode.API_AVATAR_SUCCESS, + data = avatarService.identiconBase64(avatarBaseParam) + ) + + /** + * Get GitHub avatar + * + * @param avatarGitHubParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarGitHubParam + * @see ByteArray + */ + @Operation(summary = "GitHub 头像") + @GetMapping("/github", produces = [MediaType.IMAGE_PNG_VALUE]) + fun github(@Valid avatarGitHubParam: AvatarGitHubParam?): ByteArray = + avatarService.github(avatarGitHubParam) + + /** + * Get GitHub avatar as base64 + * + * @param avatarGitHubParam Avatar base parameters + * @return Response object includes avatar base64 string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarGitHubParam + * @see ResponseResult + * @see AvatarBase64Vo + */ + @Operation(summary = "GitHub 头像 Base64") + @GetMapping("/github/base64") + fun githubBase64( + @Valid avatarGitHubParam: AvatarGitHubParam? + ): ResponseResult = + ResponseResult.success( + ResponseCode.API_AVATAR_SUCCESS, + data = avatarService.githubBase64(avatarGitHubParam) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/AuthenticationController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/AuthenticationController.kt new file mode 100644 index 0000000..9808fba --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/AuthenticationController.kt @@ -0,0 +1,233 @@ +package top.fatweb.oxygen.api.controller.permission + +import io.swagger.v3.oas.annotations.Operation +import jakarta.servlet.http.HttpServletRequest +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.permission.* +import top.fatweb.oxygen.api.service.permission.IAuthenticationService +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.permission.LoginVo +import top.fatweb.oxygen.api.vo.permission.RegisterVo +import top.fatweb.oxygen.api.vo.permission.TokenVo +import top.fatweb.oxygen.api.vo.permission.TwoFactorVo + +/** + * Authentication controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IAuthenticationService + */ +@BaseController(name = "身份认证", description = "身份认证相关接口") +class AuthenticationController( + private val authenticationService: IAuthenticationService +) { + /** + * Register + * + * @param registerParam Register parameters + * @return Response object includes user ID + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RegisterParam + * @see ResponseResult + * @see RegisterVo + */ + @Trim + @Operation(summary = "注册") + @PostMapping("/register") + fun register( + request: HttpServletRequest, + @Valid @RequestBody registerParam: RegisterParam + ): ResponseResult = ResponseResult.success( + ResponseCode.PERMISSION_REGISTER_SUCCESS, + data = authenticationService.register(request, registerParam) + ) + + + /** + * Send verify email + * + * @return Response object includes resend result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "发送验证邮件") + @PostMapping("/resend") + fun resend(): ResponseResult { + authenticationService.resend() + + return ResponseResult.success(ResponseCode.PERMISSION_RESEND_SUCCESS) + } + + /** + * Verify email + * + * @param verifyParam Verify parameters + * @return Response object includes verify result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see VerifyParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "验证邮箱") + @PostMapping("/verify") + fun verify(@Valid @RequestBody verifyParam: VerifyParam): ResponseResult { + authenticationService.verify(verifyParam) + + return ResponseResult.success(ResponseCode.PERMISSION_VERIFY_SUCCESS) + } + + /** + * Forget password + * + * @param request + * @param forgetParam Forget parameters + * @return Response object includes forget result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see ForgetParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "忘记密码") + @PostMapping("/forget") + fun forget(request: HttpServletRequest, @Valid @RequestBody forgetParam: ForgetParam): ResponseResult { + authenticationService.forget(request, forgetParam) + + return ResponseResult.success(ResponseCode.PERMISSION_FORGET_SUCCESS) + } + + /** + * Retrieve password + * + * @param request + * @param retrieveParam Retrieve parameters + * @return Response object includes retrieve result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see RetrieveParam + * @see ResponseResult + */ + @Operation(summary = "找回密码") + @PostMapping("/retrieve") + fun retrieve( + request: HttpServletRequest, + @Valid @RequestBody retrieveParam: RetrieveParam + ): ResponseResult { + authenticationService.retrieve(request, retrieveParam) + + return ResponseResult.success(ResponseCode.PERMISSION_RETRIEVE_SUCCESS) + } + + /** + * Login + * + * @param request + * @param loginParam Login parameters + * @return Response object includes login result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see LoginParam + * @see ResponseResult + * @see LoginVo + */ + @Trim + @Operation(summary = "登录") + @PostMapping("/login") + fun login(request: HttpServletRequest, @Valid @RequestBody loginParam: LoginParam): ResponseResult = + ResponseResult.success( + ResponseCode.PERMISSION_LOGIN_SUCCESS, + "Login success", + authenticationService.login(request, loginParam) + ) + + /** + * Create two-factor + * + * @return Response object includes two-factor QR code + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see TwoFactorVo + */ + @Operation(summary = "创建双因素验证码") + @GetMapping("/two-factor") + fun createTwoFactor(): ResponseResult = + ResponseResult.success(data = authenticationService.createTwoFactor()) + + /** + * Validate two-factor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Operation(summary = "验证双因素") + @PostMapping("/two-factor") + fun validateTwoFactor(@RequestBody @Valid twoFactorValidateParam: TwoFactorValidateParam): ResponseResult = + if (authenticationService.validateTwoFactor(twoFactorValidateParam)) ResponseResult.success() + else ResponseResult.fail() + + /** + * Remove two-factor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Operation(summary = "移除双因素") + @DeleteMapping("/two-factor") + fun removeTwoFactor(@RequestBody @Valid twoFactorRemoveParam: TwoFactorRemoveParam): ResponseResult = + if (authenticationService.removeTwoFactor(twoFactorRemoveParam)) ResponseResult.success() + else ResponseResult.fail() + + + /** + * Logout + * + * @param request + * @return Response object includes logout result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see ResponseResult + */ + @Operation(summary = "登出") + @PostMapping("/logout") + fun logout(request: HttpServletRequest): ResponseResult = + when (authenticationService.logout(WebUtil.getToken(request))) { + true -> ResponseResult.success(ResponseCode.PERMISSION_LOGOUT_SUCCESS, "Logout success", null) + false -> ResponseResult.fail(ResponseCode.PERMISSION_LOGOUT_FAILED, "Logout failed", null) + } + + /** + * Renew token + * + * @param request + * @return Response object includes new token + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see ResponseResult + * @see TokenVo + */ + @Operation(summary = "更新 Token") + @GetMapping("/token") + fun renewToken(request: HttpServletRequest): ResponseResult = ResponseResult.success( + ResponseCode.PERMISSION_TOKEN_RENEW_SUCCESS, + "Token renew success", + authenticationService.renewToken(WebUtil.getToken(request)) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/GroupController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/GroupController.kt new file mode 100644 index 0000000..549b4c8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/GroupController.kt @@ -0,0 +1,174 @@ +package top.fatweb.oxygen.api.controller.permission + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.permission.group.* +import top.fatweb.oxygen.api.service.permission.IGroupService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo +import top.fatweb.oxygen.api.vo.permission.base.GroupVo + +/** + * Group management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IGroupService + */ +@BaseController(path = ["/system/group"], name = "用户组管理", description = "用户组管理相关接口") +class GroupController( + val groupService: IGroupService +) { + /** + * Get group by ID + * + * @param id Group ID + * @return Response object includes group information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see GroupWithRoleVo + */ + @Operation(summary = "获取单个用户组") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:group:query:one')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = groupService.getOne(id)) + + /** + * Get group paging information + * + * @param groupGetParam Get group parameters + * @return Response object includes group paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupGetParam + * @see ResponseResult + * @see PageVo + * @see GroupWithRoleVo + */ + @Trim + @Operation(summary = "获取用户组") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:group:query:all')") + fun get(@Valid groupGetParam: GroupGetParam?): ResponseResult> = + ResponseResult.databaseSuccess( + data = groupService.getPage(groupGetParam) + ) + + /** + * Get group list + * + * @return Response object includes group list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see GroupVo + */ + @Operation(summary = "获取用户组列表") + @GetMapping("/list") + @PreAuthorize("hasAnyAuthority('system:group:query:list', 'system:user:add:one', 'system:user:modify:one')") + fun list(): ResponseResult> = + ResponseResult.databaseSuccess( + data = groupService.getList() + ) + + /** + * Add group + * + * @param groupAddParam Add group parameters + * @return Response object includes group information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupAddParam + * @see ResponseResult + * @see GroupVo + */ + @Trim + @Operation(summary = "添加用户组") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:group:add:one')") + fun add(@Valid @RequestBody groupAddParam: GroupAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, data = groupService.add(groupAddParam) + ) + + /** + * Update group + * + * @param groupUpdateParam Update group parameters + * @return Response object includes group information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateParam + * @see ResponseResult + * @see GroupVo + */ + @Trim + @Operation(summary = "修改用户组") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:group:modify:one')") + fun update(@Valid @RequestBody groupUpdateParam: GroupUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, data = groupService.update(groupUpdateParam) + ) + + /** + * Update status of group + * + * @param groupUpdateStatusParam Update status of group parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateStatusParam + * @see ResponseResult + */ + @Operation(summary = "修改用户组状态") + @PatchMapping + @PreAuthorize("hasAnyAuthority('system:group:modify:status')") + fun updateStatus(@Valid @RequestBody groupUpdateStatusParam: GroupUpdateStatusParam): ResponseResult { + groupService.status(groupUpdateStatusParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + } + + /** + * Delete group by ID + * + * @param id Group ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除用户组") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:group:delete:one')") + fun delete(@PathVariable id: Long): ResponseResult { + groupService.deleteOne(id) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } + + /** + * Delete group by list + * + * @param groupDeleteParam Delete group parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupDeleteParam + * @see ResponseResult + */ + @Operation(summary = "批量删除用户组") + @DeleteMapping + @PreAuthorize("hasAnyAuthority('system:group:delete:multiple')") + fun deleteList(@Valid @RequestBody groupDeleteParam: GroupDeleteParam): ResponseResult { + groupService.delete(groupDeleteParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/PowerController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/PowerController.kt new file mode 100644 index 0000000..f4bbd0d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/PowerController.kt @@ -0,0 +1,35 @@ +package top.fatweb.oxygen.api.controller.permission + +import io.swagger.v3.oas.annotations.Operation +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.service.permission.IPowerService +import top.fatweb.oxygen.api.vo.permission.PowerSetVo + +/** + * Power management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPowerService + */ +@BaseController(path = ["/system/power"], name = "权限管理", description = "权限管理相关接口") +class PowerController( + private val powerService: IPowerService +) { + /** + * Get power list + * + * @return Response object includes power list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see PowerSetVo + */ + @Operation(summary = "获取权限列表") + @GetMapping("/list") + @PreAuthorize("hasAnyAuthority('system:power:query:list', 'system:role:add:one', 'system:role:modify:one')") + fun getList(): ResponseResult = ResponseResult.databaseSuccess(data = powerService.getList()) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/RoleController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/RoleController.kt new file mode 100644 index 0000000..71c16a1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/RoleController.kt @@ -0,0 +1,174 @@ +package top.fatweb.oxygen.api.controller.permission + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.permission.role.* +import top.fatweb.oxygen.api.service.permission.IRoleService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo + +/** + * Role management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IRoleService + */ +@BaseController(path = ["/system/role"], name = "角色管理", description = "角色管理相关接口") +class RoleController( + private val roleService: IRoleService +) { + /** + * Get role by ID + * + * @param id Role ID + * @return Response object includes role information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see RoleWithPowerVo + */ + @Operation(summary = "获取单个角色") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:role:query:one')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = roleService.getOne(id)) + + /** + * Get role paging information + * + * @param roleGetParam Get role parameters + * @return Response object includes role paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleGetParam + * @see ResponseResult + * @see RoleWithPowerVo + */ + @Trim + @Operation(summary = "获取角色") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:role:query:all')") + fun get(roleGetParam: RoleGetParam?): ResponseResult> = + ResponseResult.databaseSuccess( + data = roleService.getPage(roleGetParam) + ) + + /** + * Get role list + * + * @return Response object includes role list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see RoleVo + */ + @Operation(summary = "获取角色列表") + @GetMapping("/list") + @PreAuthorize("hasAnyAuthority('system:role:query:list', 'system:group:add:one', 'system:group:modify:one', 'system:user:add:one', 'system:user:modify:one')") + fun list(): ResponseResult> { + return ResponseResult.databaseSuccess( + data = roleService.getList() + ) + } + + /** + * Add role + * + * @param roleAddParam Add role parameters + * @return Response object includes role information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleAddParam + * @see ResponseResult + * @see RoleVo + */ + @Trim + @Operation(summary = "添加角色") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:role:add:one')") + fun add(@Valid @RequestBody roleAddParam: RoleAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, data = roleService.add(roleAddParam) + ) + + /** + * Update role + * + * @param roleUpdateParam Update role parameters + * @return Response object includes role information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateParam + * @see ResponseResult + * @see RoleVo + */ + @Trim + @Operation(summary = "修改角色") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:role:modify:one')") + fun update(@Valid @RequestBody roleUpdateParam: RoleUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, data = roleService.update(roleUpdateParam) + ) + + /** + * Update status of role + * + * @param roleUpdateStatusParam Update status of role parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateStatusParam + * @see ResponseResult + */ + @Operation(summary = "修改角色状态") + @PatchMapping + @PreAuthorize("hasAnyAuthority('system:role:modify:status')") + fun status(@Valid @RequestBody roleUpdateStatusParam: RoleUpdateStatusParam): ResponseResult { + roleService.status(roleUpdateStatusParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + } + + /** + * Delete role by ID + * + * @param id + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除角色") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:role:delete:one')") + fun delete(@PathVariable id: Long): ResponseResult { + roleService.deleteOne(id) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } + + /** + * Delete role by list + * + * @param roleDeleteParam Delete role parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleDeleteParam + * @see ResponseResult + */ + @Operation(summary = "批量删除角色") + @DeleteMapping + @PreAuthorize("hasAnyAuthority('system:role:delete:multiple')") + fun deleteList(@Valid @RequestBody roleDeleteParam: RoleDeleteParam): ResponseResult { + roleService.delete(roleDeleteParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/UserController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/UserController.kt new file mode 100644 index 0000000..ca9b504 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/permission/UserController.kt @@ -0,0 +1,223 @@ +package top.fatweb.oxygen.api.controller.permission + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.permission.user.* +import top.fatweb.oxygen.api.service.permission.IUserService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo + +/** + * User management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IUserService + */ +@BaseController(path = ["/system/user"], name = "用户管理", description = "用户管理相关接口") +class UserController( + private val userService: IUserService +) { + /** + * Get current user information + * + * @return Response object includes user information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see UserWithPowerInfoVo + */ + @Operation(summary = "获取当前用户信息") + @GetMapping("/info") + fun getInfo(): ResponseResult = + ResponseResult.databaseSuccess(data = userService.getInfo()) + + /** + * Get basic user information + * + * @param username Username + * @return Response object includes user basic information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see UserWithPowerInfoVo + */ + @Trim + @Operation(summary = "获取指定用户基本信息") + @GetMapping("/info/{username}") + fun getBasicInfo(@PathVariable username: String): ResponseResult = + ResponseResult.databaseSuccess(data = userService.getBasicInfo(username.trim())) + + /** + * Update current user information + * + * @param userInfoUpdateParam Update user information parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoUpdateParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "更新当前用户信息") + @PatchMapping("info") + fun updateInfo(@RequestBody @Valid userInfoUpdateParam: UserInfoUpdateParam): ResponseResult = + if (userService.updateInfo(userInfoUpdateParam)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + else ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_FAILED) + + /** + * Change password + * + * @param userChangePasswordParam User change password parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserChangePasswordParam + * @see ResponseResult + */ + @Operation(summary = "更改密码") + @PostMapping("info") + fun password(@RequestBody @Valid userChangePasswordParam: UserChangePasswordParam): ResponseResult { + userService.password(userChangePasswordParam) + + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + } + + /** + * Get user by ID + * + * @param id User ID + * @return Response object includes user information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see UserWithRoleInfoVo + */ + @Operation(summary = "获取单个用户") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:user:query:one')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = userService.getOne(id)) + + /** + * Get user paging information + * + * @param userGetParam Get user parameters + * @return Response object includes user paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserGetParam + * @see ResponseResult + * @see UserWithRoleInfoVo + */ + @Trim + @Operation(summary = "获取用户") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:user:query:all')") + fun get(@Valid userGetParam: UserGetParam?): ResponseResult> = + ResponseResult.databaseSuccess( + data = userService.getPage(userGetParam) + ) + + /** + * Add user + * + * @param userAddParam Add user parameters + * @return Response object includes user information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserAddParam + * @see ResponseResult + * @see UserWithPasswordRoleInfoVo + */ + @Trim + @Operation(summary = "添加用户") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:user:add:one')") + fun add(@Valid @RequestBody userAddParam: UserAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, data = userService.add(userAddParam) + ) + + /** + * Update user + * + * @param userUpdateParam Update user parameters + * @return Response object includes user information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserUpdateParam + * @see ResponseResult + * @see UserWithRoleInfoVo + */ + @Trim + @Operation(summary = "修改用户") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:user:modify:one')") + fun update(@Valid @RequestBody userUpdateParam: UserUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, data = userService.update(userUpdateParam) + ) + + /** + * Update user password + * + * @param userUpdatePasswordParam Update user password parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserUpdatePasswordParam + * @see ResponseResult + */ + @Operation(summary = "修改密码") + @PatchMapping + @PreAuthorize("hasAnyAuthority('system:user:modify:password')") + fun password(@Valid @RequestBody userUpdatePasswordParam: UserUpdatePasswordParam): ResponseResult { + userService.password(userUpdatePasswordParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + } + + /** + * Delete user by ID + * + * @param id User ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除用户") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:user:delete:one')") + fun delete(@PathVariable id: Long): ResponseResult { + userService.deleteOne(id) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } + + /** + * Delete user by list + * + * @param userDeleteParam Delete user parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserDeleteParam + * @see ResponseResult + */ + @Operation(summary = "批量删除用户") + @DeleteMapping + @PreAuthorize("hasAnyAuthority('system:user:delete:multiple')") + fun deleteList(@Valid @RequestBody userDeleteParam: UserDeleteParam): ResponseResult { + userService.delete(userDeleteParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SettingsController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SettingsController.kt new file mode 100644 index 0000000..c41a6a0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SettingsController.kt @@ -0,0 +1,221 @@ +package top.fatweb.oxygen.api.controller.system + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.system.* +import top.fatweb.oxygen.api.service.system.ISensitiveWordService +import top.fatweb.oxygen.api.service.system.ISettingsService +import top.fatweb.oxygen.api.vo.system.BaseSettingsVo +import top.fatweb.oxygen.api.vo.system.MailSettingsVo +import top.fatweb.oxygen.api.vo.system.SensitiveWordVo +import top.fatweb.oxygen.api.vo.system.TwoFactorSettingsVo + +/** + * System settings management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ISettingsService + * @see ISensitiveWordService + */ +@BaseController(path = ["/system/settings"], name = "系统设置", description = "系统设置相关接口") +class SettingsController( + private val settingsService: ISettingsService, + private val sensitiveWordService: ISensitiveWordService +) { + /** + * Get base settings + * + * @return Response object includes base settings information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see BaseSettingsVo + */ + @Operation(summary = "获取基础设置") + @GetMapping("/base") + @PreAuthorize("hasAnyAuthority('system:settings:query:base')") + fun getApp(): ResponseResult = + ResponseResult.success(data = settingsService.getBase()) + + /** + * Update base settings + * + * @param baseSettingsParam Base settings parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseSettingsParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "更新基础设置") + @PutMapping("/base") + @PreAuthorize("hasAnyAuthority('system:settings:modify:base')") + fun updateApp(@RequestBody baseSettingsParam: BaseSettingsParam): ResponseResult { + settingsService.updateBase(baseSettingsParam) + return ResponseResult.success() + } + + /** + * Get mail settings + * + * @return Response object includes mail settings + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see MailSettingsVo + */ + @Operation(summary = "获取邮件设置") + @GetMapping("/mail") + @PreAuthorize("hasAnyAuthority('system:settings:query:mail')") + fun getMail(): ResponseResult = + ResponseResult.success(data = settingsService.getMail()) + + /** + * Update mail settings + * + * @param mailSettingsParam Mail settings parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSettingsParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "更新邮件设置") + @PutMapping("/mail") + @PreAuthorize("hasAnyAuthority('system:settings:modify:mail')") + fun updateMail(@RequestBody mailSettingsParam: MailSettingsParam): ResponseResult { + settingsService.updateMail(mailSettingsParam) + return ResponseResult.success() + } + + /** + * Send mail test + * + * @param mailSendParam Mail send parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSendParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "邮件发送测试") + @PostMapping("/mail") + @PreAuthorize("hasAnyAuthority('system:settings:modify:mail')") + fun sendMail(@RequestBody @Valid mailSendParam: MailSendParam): ResponseResult { + settingsService.sendMail(mailSendParam) + return ResponseResult.success() + } + + /** + * Get sensitive word settings + * + * @return Response object includes sensitive word settings information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see SensitiveWordVo + */ + @Operation(summary = "获取敏感词配置") + @GetMapping("/sensitive") + @PreAuthorize("hasAnyAuthority('system:settings:query:sensitive')") + fun getSensitive(): ResponseResult> = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_SELECT_SUCCESS, data = sensitiveWordService.get()) + + /** + * Add sensitive word + * + * @param sensitiveWordAddParam Add sensitive word settings parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordAddParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "添加敏感词") + @PostMapping("/sensitive") + @PreAuthorize("hasAnyAuthority('system:settings:modify:sensitive')") + fun addSensitive(@RequestBody @Valid sensitiveWordAddParam: SensitiveWordAddParam): ResponseResult { + sensitiveWordService.add(sensitiveWordAddParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_INSERT_SUCCESS) + } + + /** + * Update sensitive word + * + * @param sensitiveWordUpdateParam Update sensitive word settings parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordUpdateParam + * @see ResponseResult + */ + @Operation(summary = "修改敏感词") + @PutMapping("/sensitive") + @PreAuthorize("hasAnyAuthority('system:settings:modify:sensitive')") + fun updateSensitive(@RequestBody sensitiveWordUpdateParam: SensitiveWordUpdateParam): ResponseResult { + sensitiveWordService.update(sensitiveWordUpdateParam) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS) + } + + /** + * Delete sensitive word + * + * @see id Sensitive word ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除敏感词") + @DeleteMapping("/sensitive/{id}") + @PreAuthorize("hasAnyAuthority('system:settings:modify:sensitive')") + fun deleteSensitive(@PathVariable id: Long): ResponseResult { + sensitiveWordService.delete(id) + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } + + /** + * Get two-factor settings + * + * @return Response object includes two-factor settings information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see TwoFactorSettingsVo + */ + @Operation(summary = "获取双因素设置") + @GetMapping("/two-factor") + @PreAuthorize("hasAnyAuthority('system:settings:query:two-factor')") + fun getTwoFactor(): ResponseResult = + ResponseResult.success(data = settingsService.getTwoFactor()) + + /** + * Update two-factor settings + * + * @param twoFactorSettingsParam Two-factor settings parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorSettingsParam + * @see ResponseResult + */ + @Trim + @Operation(summary = "更新双因素设置") + @PutMapping("/two-factor") + @PreAuthorize("hasAnyAuthority('system:settings:modify:two-factor')") + fun updateTwoFactor(@RequestBody twoFactorSettingsParam: TwoFactorSettingsParam): ResponseResult { + settingsService.updateTwoFactor(twoFactorSettingsParam) + return ResponseResult.success() + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/system/StatisticsController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/StatisticsController.kt new file mode 100644 index 0000000..a04d961 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/StatisticsController.kt @@ -0,0 +1,113 @@ +package top.fatweb.oxygen.api.controller.system + +import io.swagger.v3.oas.annotations.Operation +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.system.ActiveInfoGetParam +import top.fatweb.oxygen.api.param.system.OnlineInfoGetParam +import top.fatweb.oxygen.api.service.system.IStatisticsService +import top.fatweb.oxygen.api.vo.system.* + +/** + * Statistics management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IStatisticsService + */ +@BaseController(path = ["/system/statistics"], name = "统计接口", description = "系统信息统计相关接口") +class StatisticsController( + private val statisticService: IStatisticsService +) { + /** + * Get software information + * + * @return Response object includes software information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see SoftwareInfoVo + */ + @Operation(summary = "获取软件信息") + @GetMapping("/software") + @PreAuthorize("hasAnyAuthority('system:statistics:query:base')") + fun software(): ResponseResult = ResponseResult.success(data = statisticService.software()) + + /** + * Get hardware information + * + * @return Response object includes hardware information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see HardwareInfoVo + */ + @Operation(summary = "获取硬件信息") + @GetMapping("/hardware") + @PreAuthorize("hasAnyAuthority('system:statistics:query:base')") + fun hardware(): ResponseResult = ResponseResult.success(data = statisticService.hardware()) + + /** + * Get CPU information + * + * @return Response object includes CPU information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see CpuInfoVo + */ + @Operation(summary = "获取 CPU 信息") + @GetMapping("/cpu") + @PreAuthorize("hasAnyAuthority('system:statistics:query:real')") + fun cpu(): ResponseResult = ResponseResult.success(data = statisticService.cpu()) + + /** + * Get storage information + * + * @return Response object includes storage information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see StorageInfoVo + */ + @Operation(summary = "获取存储信息") + @GetMapping("/storage") + @PreAuthorize("hasAnyAuthority('system:statistics:query:real')") + fun storage(): ResponseResult = ResponseResult.success(data = statisticService.storage()) + + /** + * Get the history of online users information + * + * @param onlineInfoGetParam Get online information parameters + * @return Response object includes online user information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see OnlineInfoGetParam + * @see ResponseResult + * @see OnlineInfoVo + */ + @Operation(summary = "获取在线用户数量信息") + @GetMapping("/online") + @PreAuthorize("hasAnyAuthority('system:statistics:query:usage')") + fun online(onlineInfoGetParam: OnlineInfoGetParam?): ResponseResult = + ResponseResult.success(data = statisticService.online(onlineInfoGetParam)) + + /** + * Get the history of active information + * + * @param activeInfoGetParam Get active information parameters + * @return Response object includes history of active information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ActiveInfoGetParam + * @see ResponseResult + * @see ActiveInfoVo + */ + @Operation(summary = "获取用户活跃信息") + @GetMapping("/active") + @PreAuthorize("hasAnyAuthority('system:statistics:query:usage')") + fun active(activeInfoGetParam: ActiveInfoGetParam): ResponseResult = + ResponseResult.success(data = statisticService.active(activeInfoGetParam)) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SysLogController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SysLogController.kt new file mode 100644 index 0000000..8cc0713 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/system/SysLogController.kt @@ -0,0 +1,47 @@ +package top.fatweb.oxygen.api.controller.system + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.system.SysLogGetParam +import top.fatweb.oxygen.api.service.system.ISysLogService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.system.SysLogVo + +/** + * System log viewer controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ISysLogService + */ +@BaseController(path = ["/system/log"], name = "系统日志", description = "系统日志相关接口") +class SysLogController( + private val sysLogService: ISysLogService +) { + /** + * Get system log in page + * + * @param sysLogGetParam Get system log parameters + * @return Response object includes system log in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SysLogGetParam + * @see ResponseResult + * @see SysLogVo + */ + @Trim + @Operation(summary = "获取") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:log:query:all')") + fun get(@Valid sysLogGetParam: SysLogGetParam?): ResponseResult> { + return ResponseResult.success( + ResponseCode.DATABASE_SELECT_SUCCESS, data = sysLogService.getPage(sysLogGetParam) + ) + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/BaseController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/BaseController.kt new file mode 100644 index 0000000..8741981 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/BaseController.kt @@ -0,0 +1,136 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.tool.ToolBaseAddParam +import top.fatweb.oxygen.api.param.tool.ToolBaseGetParam +import top.fatweb.oxygen.api.param.tool.ToolBaseUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolBaseService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolBaseVo + +/** + * Tool base management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolBaseService + */ +@BaseController(path = ["/system/tool/base"], name = "工具基板管理", description = "工具基板管理相关接口") +class BaseController( + private val toolBaseService: IToolBaseService +) { + /** + * Get tool base by ID + * + * @param id Tool base ID + * @return Response object includes tool base information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolBaseVo + */ + @Operation(summary = "获取单个基板") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:query:base')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = toolBaseService.getOne(id)) + + /** + * Get tool base paging information + * + * @param toolBaseGetParam Get tool base parameters + * @return Response object includes tool base paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseGetParam + * @see ResponseResult + * @see PageVo + * @see ToolBaseVo + */ + @Operation(summary = "获取基板") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:tool:query:base')") + fun get(toolBaseGetParam: ToolBaseGetParam?): ResponseResult> = + ResponseResult.databaseSuccess(data = toolBaseService.get(toolBaseGetParam)) + + /** + * Get tool base list + * + * @return Response object includes tool base list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolBaseVo + */ + @Operation(summary = "获取基板列表") + @GetMapping("/list") + @PreAuthorize("hasAnyAuthority('system:tool:add:template', 'system:tool:modify:template')") + fun list(): ResponseResult> = + ResponseResult.databaseSuccess(data = toolBaseService.getList()) + + + /** + * Add tool base + * + * @param toolBaseAddParam Add tool base parameters + * @return Response object includes tool base information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseAddParam + * @see ResponseResult + * @see ToolBaseVo + */ + @Trim + @Operation(summary = "新增基板") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:tool:add:base')") + fun add(@RequestBody @Valid toolBaseAddParam: ToolBaseAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, + data = toolBaseService.add(toolBaseAddParam) + ) + + /** + * Update tool base + * + * @param toolBaseUpdateParam Update tool base parameters + * @return Response object includes tool base information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseUpdateParam + * @see ResponseResult + * @see ToolBaseVo + */ + @Trim + @Operation(summary = "更新基板") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:tool:modify:base')") + fun update(@RequestBody @Valid toolBaseUpdateParam: ToolBaseUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, + data = toolBaseService.update(toolBaseUpdateParam) + ) + + /** + * Delete tool base by ID + * + * @param id Tool base ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除基板") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:delete:base')") + fun delete(@PathVariable id: Long): ResponseResult = + if (toolBaseService.delete(id)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + else ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/CategoryController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/CategoryController.kt new file mode 100644 index 0000000..460c3d4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/CategoryController.kt @@ -0,0 +1,115 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.tool.ToolCategoryAddParam +import top.fatweb.oxygen.api.param.tool.ToolCategoryUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolCategoryService +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo + +/** + * Tool category management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolCategoryService + */ +@BaseController(path = ["/system/tool/category"], name = "工具类别管理", description = "工具列别管理相关接口") +class CategoryController( + private val toolCategoryService: IToolCategoryService +) { + /** + * Get tool category by ID + * + * @param id Tool category ID + * @return Response object includes tool template information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolCategoryVo + */ + @Operation(summary = "获取单个类别") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:query:category')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = toolCategoryService.getOne(id)) + + /** + * Get tool category list + * + * @return Response object includes tool template list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolCategoryVo + */ + @Operation(summary = "获取类别") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:tool:query:category')") + fun get(): ResponseResult> = + ResponseResult.databaseSuccess(data = toolCategoryService.get()) + + /** + * Add tool category + * + * @param toolCategoryAddParam Add tool category parameters + * @return Response object includes tool category information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryAddParam + * @see ResponseResult + * @see ToolCategoryVo + */ + @Trim + @Operation(summary = "新增类别") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:tool:add:category')") + fun add(@RequestBody @Valid toolCategoryAddParam: ToolCategoryAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, + data = toolCategoryService.add(toolCategoryAddParam) + ) + + /** + * Update tool category + * + * @param toolCategoryUpdateParam Update tool category parameters + * @return Response object includes tool category information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryUpdateParam + * @see ResponseResult + * @see ToolCategoryVo + */ + @Trim + @Operation(summary = "更新类别") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:tool:modify:category')") + fun update(@RequestBody @Valid toolCategoryUpdateParam: ToolCategoryUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, + data = toolCategoryService.update(toolCategoryUpdateParam) + ) + + /** + * Delete tool category + * + * @param id Tool category ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除类别") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:delete:category')") + fun delete(@PathVariable id: Long): ResponseResult = + if (toolCategoryService.delete(id)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + else ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/EditController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/EditController.kt new file mode 100644 index 0000000..dec2efb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/EditController.kt @@ -0,0 +1,215 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolCreateParam +import top.fatweb.oxygen.api.param.tool.ToolUpdateParam +import top.fatweb.oxygen.api.param.tool.ToolUpgradeParam +import top.fatweb.oxygen.api.service.tool.IEditService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool edit controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IEditService + */ +@BaseController(path = ["/tool"], name = "工具编辑", description = "工具编辑相关接口") +class EditController( + private val editService: IEditService +) { + /** + * Get tool template list + * + * @return Response object includes tool template list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolTemplateVo + */ + @Operation(summary = "获取模板") + @GetMapping("/template") + fun getTemplate(platform: Platform): ResponseResult> = + ResponseResult.databaseSuccess(data = editService.getTemplate(platform)) + + /** + * Get tool template by ID + * + * @param id ID + * @return Response object includes tool template information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolTemplateVo + */ + @Operation(summary = "获取单个模板") + @GetMapping("/template/{id}") + fun getTemplate(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = editService.getTemplate(id)) + + /** + * Get tool category list + * + * @return Response object includes tool category list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolCategoryVo + */ + @Operation(summary = "获取类别") + @GetMapping("/category") + fun getCategory(): ResponseResult> = + ResponseResult.databaseSuccess(data = editService.getCategory()) + + /** + * Create tool + * + * @param toolCreateParam Create tool parameters + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCreateParam + * @see ResponseResult + * @see ToolVo + */ + @Trim + @Operation(summary = "创建工具") + @PostMapping + fun create(@RequestBody @Valid toolCreateParam: ToolCreateParam): ResponseResult = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_INSERT_SUCCESS, data = editService.create(toolCreateParam)) + + /** + * Upgrade tool + * + * @param toolUpgradeParam Upgrade tool parameters + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolUpgradeParam + * @see ResponseResult + * @see ToolVo + */ + @Trim + @Operation(summary = "升级工具") + @PatchMapping + fun upgrade(@RequestBody @Valid toolUpgradeParam: ToolUpgradeParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, + data = editService.upgrade(toolUpgradeParam) + ) + + /** + * Get personal tool + * + * @return Response object includes tool list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + * @see ResponseResult + * @see PageVo + * @see ToolVo + */ + @Trim + @Operation(summary = "获取个人工具") + @GetMapping + fun get(@Valid pageSortParam: PageSortParam): ResponseResult> = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_SELECT_SUCCESS, data = editService.getPage(pageSortParam)) + + /** + * Get tool detail + * + * @param username Username + * @param toolId Tool ID + * @param ver Version + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolVo + */ + @Operation(summary = "获取工具内容") + @GetMapping("/detail/{username}/{toolId}/{ver}") + fun detail( + @PathVariable username: String, + @PathVariable toolId: String, + @PathVariable ver: String, + platform: Platform + ): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_SELECT_SUCCESS, + data = editService.detail(username.trim(), toolId.trim(), ver.trim(), platform) + ) + + /** + * Update tool + * + * @param toolUpdateParam Update tool parameters + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolUpdateParam + * @see ResponseResult + * @see ToolVo + */ + @Trim + @Operation(summary = "更新工具") + @PutMapping + fun update(@RequestBody @Valid toolUpdateParam: ToolUpdateParam): ResponseResult = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS, data = editService.update(toolUpdateParam)) + + /** + * Submit tool review + * + * @param id Tool ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "提交工具审核") + @PostMapping("/{id}") + fun submit(@PathVariable id: Long): ResponseResult = + if (editService.submit(id)) ResponseResult.success(ResponseCode.TOOL_SUBMIT_SUCCESS) + else ResponseResult.fail(ResponseCode.TOOL_SUBMIT_ERROR) + + /** + * Cancel tool review + * + * @param id Tool ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "取消工具审核") + @PutMapping("/{id}") + fun cancel(@PathVariable id: Long): ResponseResult = + if (editService.cancel(id)) ResponseResult.success(ResponseCode.TOOL_CANCEL_SUCCESS) + else ResponseResult.fail(ResponseCode.TOOL_CANCEL_ERROR) + + /** + * Delete tool + * + * @param id Tool ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除工具") + @DeleteMapping("/{id}") + fun delete(@PathVariable id: Long): ResponseResult = + if (editService.delete(id)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + else ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/ManagementController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/ManagementController.kt new file mode 100644 index 0000000..bda530d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/ManagementController.kt @@ -0,0 +1,134 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.tool.ToolManagementGetParam +import top.fatweb.oxygen.api.param.tool.ToolManagementPassParam +import top.fatweb.oxygen.api.service.tool.IManagementService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IManagementService + */ +@BaseController(path = ["/system/tool"], name = "工具管理", description = "工具管理相关接口") +class ManagementController( + private val managementService: IManagementService +) { + /** + * Get tool by ID + * + * @param id Tool ID + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolVo + */ + @Operation(summary = "获取单个工具") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:query:tool')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = managementService.getOne(id)) + + /** + * Get tool paging information + * + * @param toolManagementGetParam Get tool parameters in tool management + * @return Response object includes tool paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolManagementGetParam + * @see ResponseResult + * @see PageVo + * @see ToolVo + */ + @Trim + @Operation(summary = "获取工具") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:tool:query:tool')") + fun get(toolManagementGetParam: ToolManagementGetParam): ResponseResult> = + ResponseResult.databaseSuccess(data = managementService.getPage(toolManagementGetParam)) + + /** + * Pass tool review + * + * @param id Tool ID + * @param toolManagementPassParam Pass tool parameters in tool management + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolManagementPassParam + * @see ResponseResult + * @see ToolVo + */ + @Operation(summary = "通过审核") + @PostMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:modify:tool')") + fun pass( + @PathVariable id: Long, + @RequestBody @Valid toolManagementPassParam: ToolManagementPassParam + ): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, + data = managementService.pass(id, toolManagementPassParam) + ) + + /** + * Reject tool review + * + * @param id Tool ID + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolVo + */ + @Operation(summary = "驳回审核") + @PutMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:modify:tool')") + fun reject(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS, data = managementService.reject(id)) + + /** + * Put off shelve + * + * @param id Tool ID + * @return Response object includes tool information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolVo + */ + @Operation(summary = "下架") + @PatchMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:modify:tool')") + fun offShelve(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(ResponseCode.DATABASE_UPDATE_SUCCESS, data = managementService.offShelve(id)) + + /** + * Delete tool + * + * @param id Tool ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除工具") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:delete:tool')") + fun delete(@PathVariable id: Long): ResponseResult = + if (managementService.delete(id)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + else ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/StoreController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/StoreController.kt new file mode 100644 index 0000000..f3c1e58 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/StoreController.kt @@ -0,0 +1,119 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteAddParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteRemoveParam +import top.fatweb.oxygen.api.param.tool.ToolStoreGetParam +import top.fatweb.oxygen.api.service.tool.IStoreService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool store controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IStoreService + */ +@BaseController(path = ["/tool/store"], name = "工具商店", description = "工具商店相关接口") +class StoreController( + private val storeService: IStoreService +) { + /** + * Get store tool paging information + * + * @param toolStoreGetParam Get tool parameters in tool store + * @return Response object includes store tool paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolStoreGetParam + * @see ResponseResult + * @see PageVo + * @see ToolVo + */ + @Trim + @Operation(description = "获取商店工具") + @GetMapping + fun get(@Valid toolStoreGetParam: ToolStoreGetParam): ResponseResult> = + ResponseResult.databaseSuccess(data = storeService.getPage(toolStoreGetParam)) + + /** + * Get store tool paging information by username + * + * @param username Username + * @param pageSortParam Page sort parameters + * @return Response object includes store tool paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + * @see ResponseResult + * @see PageVo + * @see ToolVo + */ + @Trim + @Operation(description = "获取商店指定用户工具") + @GetMapping("/{username}") + fun get(@PathVariable username: String, @Valid pageSortParam: PageSortParam): ResponseResult> = + ResponseResult.databaseSuccess(data = storeService.getPage(pageSortParam, username.trim())) + + /** + * Add favorite tool + * + * @param toolFavoriteAddParam Add favorite tool parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolFavoriteAddParam + * @see ResponseResult + */ + @Operation(summary = "收藏工具") + @PostMapping("/favorite") + fun addFavorite(@RequestBody toolFavoriteAddParam: ToolFavoriteAddParam): ResponseResult { + storeService.addFavorite(toolFavoriteAddParam) + + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_INSERT_SUCCESS) + } + + /** + * Remove favorite tool + * + * @param toolFavoriteRemoveParam Remove favorite tool parameters + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolFavoriteRemoveParam + * @see ResponseResult + */ + @Operation(summary = "收藏工具") + @DeleteMapping("/favorite") + fun addFavorite(@RequestBody toolFavoriteRemoveParam: ToolFavoriteRemoveParam): ResponseResult { + storeService.removeFavorite(toolFavoriteRemoveParam) + + return ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + } + + /** + * Get favorite tool + * + * @param pageSortParam Page sort parameters + * @return Response object includes favorite tool paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + * @see ResponseResult + * @see PageVo + * @see ToolVo + */ + @Trim + @Operation(summary = "获取收藏工具") + @GetMapping("/favorite") + fun getFavorite(@Valid pageSortParam: PageSortParam): ResponseResult> = + ResponseResult.databaseSuccess(data = storeService.getFavorite(pageSortParam)) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/TemplateController.kt b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/TemplateController.kt new file mode 100644 index 0000000..3cbed35 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/controller/tool/TemplateController.kt @@ -0,0 +1,120 @@ +package top.fatweb.oxygen.api.controller.tool + +import io.swagger.v3.oas.annotations.Operation +import jakarta.validation.Valid +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.* +import top.fatweb.oxygen.api.annotation.BaseController +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.param.tool.ToolTemplateAddParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateGetParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolTemplateService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo + +/** + * Tool template management controller + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolTemplateService + */ +@BaseController(path = ["/system/tool/template"], name = "工具模板管理", description = "工具模板管理相关接口") +class TemplateController( + private val toolTemplateService: IToolTemplateService +) { + /** + * Get tool template by ID + * + * @param id Tool template ID + * @return Response object includes tool template information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + * @see ToolTemplateVo + */ + @Operation(summary = "获取单个模板") + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:query:template')") + fun getOne(@PathVariable id: Long): ResponseResult = + ResponseResult.databaseSuccess(data = toolTemplateService.getOne(id)) + + /** + * Get tool template paging information + * + * @param toolTemplateGetParam Get tool template parameters + * @return Response object includes tool template paging information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateGetParam + * @see ResponseResult + * @see PageVo + * @see ToolTemplateVo + */ + @Operation(summary = "获取模板") + @GetMapping + @PreAuthorize("hasAnyAuthority('system:tool:query:template')") + fun get(toolTemplateGetParam: ToolTemplateGetParam?): ResponseResult> = + ResponseResult.databaseSuccess(data = toolTemplateService.get(toolTemplateGetParam)) + + /** + * Add tool template + * + * @param toolTemplateAddParam Add tool template parameters + * @return Response object includes tool template information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateAddParam + * @see ResponseResult + * @see ToolTemplateVo + */ + @Trim + @Operation(summary = "添加模板") + @PostMapping + @PreAuthorize("hasAnyAuthority('system:tool:add:template')") + fun add(@RequestBody @Valid toolTemplateAddParam: ToolTemplateAddParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_INSERT_SUCCESS, + data = toolTemplateService.add(toolTemplateAddParam) + ) + + /** + * Update tool template + * + * @param toolTemplateUpdateParam Update tool template parameters + * @return Response object includes tool template information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateUpdateParam + * @see ResponseResult + * @see ToolTemplateVo + */ + @Trim + @Operation(summary = "更新模板") + @PutMapping + @PreAuthorize("hasAnyAuthority('system:tool:modify:template')") + fun update(@RequestBody @Valid toolTemplateUpdateParam: ToolTemplateUpdateParam): ResponseResult = + ResponseResult.databaseSuccess( + ResponseCode.DATABASE_UPDATE_SUCCESS, + data = toolTemplateService.update(toolTemplateUpdateParam) + ) + + /** + * Delete tool template + * + * @param id Tool template ID + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseResult + */ + @Operation(summary = "删除模板") + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('system:tool:delete:template')") + fun delete(@PathVariable id: Long): ResponseResult = + if (toolTemplateService.delete(id)) ResponseResult.databaseSuccess(ResponseCode.DATABASE_DELETE_SUCCESS) + else ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/FuncConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/FuncConverter.kt new file mode 100644 index 0000000..ed5c227 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/FuncConverter.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.Func +import top.fatweb.oxygen.api.vo.permission.base.FuncVo + +/** + * Function converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object FuncConverter { + /** + * Convert Func object into FuncVo object + * + * @param func Func object + * @return FuncVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Func + * @see FuncVo + */ + fun funcToFuncVo(func: Func) = FuncVo( + id = func.id, + name = func.name, + parentId = func.parentId, + menuId = func.menuId + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/GroupConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/GroupConverter.kt new file mode 100644 index 0000000..0f544e9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/GroupConverter.kt @@ -0,0 +1,124 @@ +package top.fatweb.oxygen.api.converter.permission + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.oxygen.api.entity.permission.Group +import top.fatweb.oxygen.api.entity.permission.Role +import top.fatweb.oxygen.api.param.permission.group.GroupAddParam +import top.fatweb.oxygen.api.param.permission.group.GroupUpdateParam +import top.fatweb.oxygen.api.param.permission.group.GroupUpdateStatusParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo +import top.fatweb.oxygen.api.vo.permission.base.GroupVo + +/** + * Group converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object GroupConverter { + /** + * Convert Group object into GroupVo object + * + * @param group Group object + * @return GroupVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Group + * @see GroupVo + */ + fun groupToGroupVo(group: Group) = GroupVo( + id = group.id, + name = group.name, + enable = group.enable?.let { it == 1}, + createTime = group.createTime, + updateTime = group.updateTime + ) + + /** + * Convert Group object into GroupWithRoleVo object + * + * @param group Group object + * @return GroupWithRoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Group + * @see GroupWithRoleVo + */ + fun groupToGroupWithRoleVo(group: Group) = GroupWithRoleVo( + id = group.id, + name = group.name, + enable = group.enable?.let { it == 1}, + createTime = group.createTime, + updateTime = group.updateTime, + roles = group.roles?.map(RoleConverter::roleToRoleVo) + ) + + /** + * Convert IPage object into PageVo object + * + * @param groupPage IPage object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see Group + * @see PageVo + * @see GroupWithRoleVo + */ + fun groupPageToGroupWithRolePageVo(groupPage: IPage) = PageVo( + total = groupPage.total, + pages = groupPage.pages, + size = groupPage.size, + current = groupPage.current, + records = groupPage.records.map(::groupToGroupWithRoleVo) + ) + + /** + * Convert GroupAddParam object into Group object + * + * @param groupAddParam GroupAddParam object + * @return Group object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupAddParam + * @see Group + */ + fun groupAddParamToGroup(groupAddParam: GroupAddParam) = Group().apply { + name = groupAddParam.name + enable = if (groupAddParam.enable) 1 else 0 + roles = groupAddParam.roleIds?.map { Role().apply { id = it } } + } + + /** + * Convert GroupUpdateParam object into Group object + * + * @param groupUpdateParam GroupUpdateParam object + * @return Group object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateParam + * @see Group + */ + fun groupUpdateParamToGroup(groupUpdateParam: GroupUpdateParam) = Group().apply { + id = groupUpdateParam.id + name = groupUpdateParam.name + enable = if (groupUpdateParam.enable) 1 else 0 + roles = groupUpdateParam.roleIds?.map { Role().apply { id = it } } + } + + /** + * Convert GroupUpdateStatusParam object into Group object + * + * @param groupUpdateStatusParam GroupUpdateStatusParam object + * @return Group object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateStatusParam + * @see Group + */ + fun groupUpdateStatusParamToGroup(groupUpdateStatusParam: GroupUpdateStatusParam) = Group().apply { + id = groupUpdateStatusParam.id + enable = if (groupUpdateStatusParam.enable) 1 else 0 + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/MenuConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/MenuConverter.kt new file mode 100644 index 0000000..ef573d1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/MenuConverter.kt @@ -0,0 +1,30 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.Menu +import top.fatweb.oxygen.api.vo.permission.base.MenuVo + +/** + * Menu converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object MenuConverter { + /** + * Convert Menu object into MenuVo object + * + * @param menu Menu object + * @return MenuVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Menu + * @see MenuVo + */ + fun menuToMenuVo(menu: Menu) = MenuVo( + id = menu.id, + name = menu.name, + url = menu.url, + parentId = menu.parentId, + moduleId = menu.moduleId + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/ModuleConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/ModuleConverter.kt new file mode 100644 index 0000000..4760c51 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/ModuleConverter.kt @@ -0,0 +1,27 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.Module +import top.fatweb.oxygen.api.vo.permission.base.ModuleVo + +/** + * Module converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ModuleConverter { + /** + * Convert Module object into ModuleVo object + * + * @param module Module object + * @return ModuleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Module + * @see ModuleVo + */ + fun moduleToModuleVo(module: Module) = ModuleVo( + id = module.id, + name = module.name + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/OperationConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/OperationConverter.kt new file mode 100644 index 0000000..dd9f0b3 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/OperationConverter.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.Operation +import top.fatweb.oxygen.api.vo.permission.base.OperationVo + +/** + * Operation converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object OperationConverter { + /** + * Convert Operation object into OperationVo object + * + * @param operation Operation object + * @return OperationVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Operation + * @see OperationVo + */ + fun operationToOperationVo(operation: Operation) = OperationVo( + id = operation.id, + name = operation.name, + code = operation.code, + funcId = operation.funcId + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/PowerConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/PowerConverter.kt new file mode 100644 index 0000000..5fea079 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/PowerConverter.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.PowerSet +import top.fatweb.oxygen.api.vo.permission.PowerSetVo + +/** + * Power converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object PowerConverter { + /** + * Convert PowerSet object into PowerSetVo object + * + * @param powerSet PowerSet object + * @return PowerSetVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PowerSet + * @see PowerSetVo + */ + fun powerSetToPowerSetVo(powerSet: PowerSet) = PowerSetVo( + moduleList = powerSet.moduleList?.map(ModuleConverter::moduleToModuleVo), + menuList = powerSet.menuList?.map(MenuConverter::menuToMenuVo), + funcList = powerSet.funcList?.map(FuncConverter::funcToFuncVo), + operationList = powerSet.operationList?.map(OperationConverter::operationToOperationVo) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/RoleConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/RoleConverter.kt new file mode 100644 index 0000000..f6bd0b7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/RoleConverter.kt @@ -0,0 +1,127 @@ +package top.fatweb.oxygen.api.converter.permission + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.oxygen.api.entity.permission.Power +import top.fatweb.oxygen.api.entity.permission.Role +import top.fatweb.oxygen.api.param.permission.role.RoleAddParam +import top.fatweb.oxygen.api.param.permission.role.RoleUpdateParam +import top.fatweb.oxygen.api.param.permission.role.RoleUpdateStatusParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo + +/** + * Role converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object RoleConverter { + /** + * Convert Role object into RoleVo object + * + * @param role Role object + * @return RoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + * @see RoleVo + */ + fun roleToRoleVo(role: Role) = RoleVo( + id = role.id, + name = role.name, + enable = role.enable?.let { it == 1}, + createTime = role.createTime, + updateTime = role.updateTime + ) + + /** + * Convert Role object into RoleWithPowerVo object + * + * @param role Role object + * @return RoleWithPowerVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + * @see RoleWithPowerVo + */ + fun roleToRoleWithPowerVo(role: Role) = RoleWithPowerVo( + id = role.id, + name = role.name, + enable = role.enable?.let { it == 1}, + createTime = role.createTime, + updateTime = role.updateTime, + modules = role.modules?.map(ModuleConverter::moduleToModuleVo), + menus = role.menus?.map(MenuConverter::menuToMenuVo), + funcs = role.funcs?.map(FuncConverter::funcToFuncVo), + operations = role.operations?.map(OperationConverter::operationToOperationVo) + ) + + /** + * Convert IPage object into PageVo object + * + * @param rolePage IPage object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see Role + * @see PageVo + * @see RoleWithPowerVo + */ + fun rolePageToRoleWithPowerPageVo(rolePage: IPage) = PageVo( + total = rolePage.total, + pages = rolePage.pages, + size = rolePage.size, + current = rolePage.current, + records = rolePage.records.map(::roleToRoleWithPowerVo) + ) + + /** + * Convert RoleAddParam object into Role object + * + * @param roleAddParam RoleAddParam object + * @return Role object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleAddParam + * @see Role + */ + fun roleAddParamToRole(roleAddParam: RoleAddParam) = Role().apply { + name = roleAddParam.name + enable = if (roleAddParam.enable) 1 else 0 + powers = roleAddParam.powerIds?.map { Power().apply { id = it } } + } + + /** + * Convert RoleUpdateParam into Role object + * + * @param roleUpdateParam RoleUpdateParam object + * @return Role object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateParam + * @see Role + */ + fun roleUpdateParamToRole(roleUpdateParam: RoleUpdateParam) = Role().apply { + id = roleUpdateParam.id + name = roleUpdateParam.name + enable = if (roleUpdateParam.enable) 1 else 0 + powers = roleUpdateParam.powerIds?.map { Power().apply { id = it } } + } + + /** + * Convert RoleUpdateStatusParam object into Role object + * + * @param roleUpdateStatusParam RoleUpdateStatusParam object + * @return Role object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateStatusParam + * @see Role + */ + fun roleUpdateStatusParamToRole(roleUpdateStatusParam: RoleUpdateStatusParam) = Role().apply { + id = roleUpdateStatusParam.id + enable = if (roleUpdateStatusParam.enable) 1 else 0 + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserConverter.kt new file mode 100644 index 0000000..dec373e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserConverter.kt @@ -0,0 +1,217 @@ +package top.fatweb.oxygen.api.converter.permission + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.avatargenerator.GitHubAvatar +import top.fatweb.oxygen.api.entity.permission.Group +import top.fatweb.oxygen.api.entity.permission.Role +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.param.permission.user.UserAddParam +import top.fatweb.oxygen.api.param.permission.user.UserUpdateParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo + +/** + * User converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object UserConverter { + /** + * Convert User object into UserWithPowerInfoVo object + * + * @param user User object + * @return UserWithPowerInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + * @see UserWithPowerInfoVo + */ + fun userToUserWithPowerInfoVo(user: User) = UserWithPowerInfoVo( + id = user.id, + username = user.username, + twoFactor = !user.twoFactor.isNullOrBlank() && !user.twoFactor!!.endsWith("?"), + verified = user.verify.isNullOrBlank(), + locking = user.locking?.let { it == 1 }, + expiration = user.expiration, + credentialsExpiration = user.credentialsExpiration, + enable = user.enable?.let { it == 1 }, + currentLoginTime = user.currentLoginTime, + currentLoginIp = user.currentLoginIp, + lastLoginTime = user.lastLoginTime, + lastLoginIp = user.lastLoginIp, + createTime = user.createTime, + updateTime = user.updateTime, + userInfo = user.userInfo?.let(UserInfoConverter::userInfoToUserInfoVo), + modules = user.modules?.map(ModuleConverter::moduleToModuleVo), + menus = user.menus?.map(MenuConverter::menuToMenuVo), + funcs = user.funcs?.map(FuncConverter::funcToFuncVo), + operations = user.operations?.map(OperationConverter::operationToOperationVo) + ) + + /** + * Convert User object into UserWithRoleInfoVo object + * + * @param user User object + * @return UserWithRoleInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + * @see UserWithRoleInfoVo + */ + fun userToUserWithRoleInfoVo(user: User) = UserWithRoleInfoVo( + id = user.id, + username = user.username, + twoFactor = !user.twoFactor.isNullOrBlank() && !user.twoFactor!!.endsWith("?"), + verify = user.verify, + locking = user.locking?.let { it == 1 }, + expiration = user.expiration, + credentialsExpiration = user.credentialsExpiration, + enable = user.enable?.let { it == 1 }, + currentLoginTime = user.currentLoginTime, + currentLoginIp = user.currentLoginIp, + lastLoginTime = user.lastLoginTime, + lastLoginIp = user.lastLoginIp, + createTime = user.createTime, + updateTime = user.updateTime, + userInfo = user.userInfo?.let(UserInfoConverter::userInfoToUserInfoVo), + roles = user.roles?.map(RoleConverter::roleToRoleVo), + groups = user.groups?.map(GroupConverter::groupToGroupVo) + ) + + /** + * Convert User object into UserWithInfoVo object + * + * @param user User object + * @return UserWithInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + * @see UserWithInfoVo + */ + fun userToUserWithInfoVo(user: User) = UserWithInfoVo( + id = user.id, + username = user.username, + twoFactor = !user.twoFactor.isNullOrBlank() && !user.twoFactor!!.endsWith("?"), + verified = user.verify.isNullOrBlank(), + locking = user.locking?.let { it == 1 }, + expiration = user.expiration, + credentialsExpiration = user.credentialsExpiration, + enable = user.enable?.let { it == 1 }, + currentLoginTime = user.currentLoginTime, + currentLoginIp = user.currentLoginIp, + lastLoginTime = user.lastLoginTime, + lastLoginIp = user.lastLoginIp, + createTime = user.createTime, + updateTime = user.updateTime, + userInfo = user.userInfo?.let(UserInfoConverter::userInfoToUserInfoVo) + ) + + /** + * Convert User object into UserWithPasswordRoleInfoVo object + * + * @param user User object + * @return UserWithPasswordRoleInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + * @see UserWithPasswordRoleInfoVo + */ + fun userToUserWithPasswordRoleInfoVo(user: User) = UserWithPasswordRoleInfoVo( + id = user.id, + username = user.username, + password = user.password, + twoFactor = !user.twoFactor.isNullOrBlank() && !user.twoFactor!!.endsWith("?"), + verify = user.verify, + locking = user.locking?.let { it == 1 }, + expiration = user.expiration, + credentialsExpiration = user.credentialsExpiration, + enable = user.enable?.let { it == 1 }, + currentLoginTime = user.currentLoginTime, + currentLoginIp = user.currentLoginIp, + lastLoginTime = user.lastLoginTime, + lastLoginIp = user.lastLoginIp, + createTime = user.createTime, + updateTime = user.updateTime, + userInfo = user.userInfo?.let(UserInfoConverter::userInfoToUserInfoVo), + roles = user.roles?.map(RoleConverter::roleToRoleVo), + groups = user.groups?.map(GroupConverter::groupToGroupVo) + ) + + /** + * Convert UserAddParam object into User object + * + * @param userAddParam UserAddParam object + * @return User object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserAddParam + * @see User + */ + fun userAddParamToUser(userAddParam: UserAddParam) = User().apply { + username = userAddParam.username + password = userAddParam.password + locking = if (userAddParam.locking) 1 else 0 + expiration = userAddParam.expiration + credentialsExpiration = userAddParam.credentialsExpiration + enable = if (userAddParam.enable) 1 else 0 + userInfo = UserInfo().apply { + nickname = userAddParam.nickname ?: userAddParam.username + avatar = userAddParam.avatar ?: GitHubAvatar.newAvatarBuilder().build() + .createAsBase64((Long.MIN_VALUE..Long.MAX_VALUE).random()) + email = userAddParam.email + } + roles = userAddParam.roleIds?.map { Role().apply { id = it } } + groups = userAddParam.groupIds?.map { Group().apply { id = it } } + } + + /** + * Convert UserUpdateParam object into User object + * + * @param userUpdateParam UserUpdateParam object + * @return User object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserUpdateParam + * @see User + */ + fun userUpdateParamToUser(userUpdateParam: UserUpdateParam) = User().apply { + id = userUpdateParam.id + username = userUpdateParam.username + locking = if (userUpdateParam.locking && userUpdateParam.id != 0L) 1 else 0 + expiration = if (userUpdateParam.id != 0L) userUpdateParam.expiration else null + credentialsExpiration = userUpdateParam.credentialsExpiration + enable = if (userUpdateParam.enable || userUpdateParam.id == 0L) 1 else 0 + userInfo = UserInfo().apply { + nickname = userUpdateParam.nickname + avatar = userUpdateParam.avatar + email = userUpdateParam.email + } + roles = if (userUpdateParam.id != 0L) userUpdateParam.roleIds?.map { Role().apply { id = it } } else null + groups = if (userUpdateParam.id != 0L) userUpdateParam.groupIds?.map { Group().apply { id = it } } else null + } + + /** + * Convert IPage object into PageVo object + * + * @param userPage IPage object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see User + * @see PageVo + * @see UserWithRoleInfoVo + */ + fun userPageToUserWithRoleInfoPageVo(userPage: IPage) = PageVo( + total = userPage.total, + pages = userPage.pages, + size = userPage.size, + current = userPage.current, + records = userPage.records.map(::userToUserWithRoleInfoVo) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserInfoConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserInfoConverter.kt new file mode 100644 index 0000000..89bf651 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/permission/UserInfoConverter.kt @@ -0,0 +1,32 @@ +package top.fatweb.oxygen.api.converter.permission + +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.vo.permission.base.UserInfoVo + +/** + * User information converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object UserInfoConverter { + /** + * Convert UserInfo object into UserInfoVo object + * + * @param userInfo UserInfo object + * @return UserInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfo + * @see UserInfoVo + */ + fun userInfoToUserInfoVo(userInfo: UserInfo) = UserInfoVo( + id = userInfo.id, + userId = userInfo.userId, + nickname = userInfo.nickname, + avatar = userInfo.avatar, + email = userInfo.email, + createTime = userInfo.createTime, + updateTime = userInfo.updateTime + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SettingsConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SettingsConverter.kt new file mode 100644 index 0000000..8f2d554 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SettingsConverter.kt @@ -0,0 +1,46 @@ +package top.fatweb.oxygen.api.converter.system + +import top.fatweb.oxygen.api.entity.system.SensitiveWord +import top.fatweb.oxygen.api.param.system.SensitiveWordAddParam +import top.fatweb.oxygen.api.vo.system.SensitiveWordVo + +/** + * Settings converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object SettingsConverter { + /** + * Convert SensitiveWord object into SensitiveWordVo object + * + * @param sensitiveWord SensitiveWord object + * @return SensitiveWordVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWord + * @see SensitiveWordVo + */ + fun sensitiveWordToSensitiveWordVo(sensitiveWord: SensitiveWord) = SensitiveWordVo( + id = sensitiveWord.id, + word = sensitiveWord.word, + useFor = sensitiveWord.useFor?.map(SensitiveWord.Use::valueOf)?.toSet(), + enable = sensitiveWord.enable?.let { it == 1} + ) + + /** + * Convert SensitiveWordAddParam object into SensitiveWord object + * + * @param sensitiveWordAddParam SensitiveWordAddParam object + * @return SensitiveWord object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordAddParam + * @see SensitiveWord + */ + fun sensitiveWordAddParamToSensitiveWord(sensitiveWordAddParam: SensitiveWordAddParam) = SensitiveWord().apply { + word = sensitiveWordAddParam.word + useFor = sensitiveWordAddParam.useFor.map(SensitiveWord.Use::code).toSet() + enable = if (sensitiveWordAddParam.enable) 1 else 0 + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SysLogConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SysLogConverter.kt new file mode 100644 index 0000000..ce7f05c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/system/SysLogConverter.kt @@ -0,0 +1,53 @@ +package top.fatweb.oxygen.api.converter.system + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.oxygen.api.entity.system.SysLog +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.system.SysLogVo + +/** + * System log converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object SysLogConverter { + /** + * Convert IPage object into PageVo object + * + * @param syslogPage IPage object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see SysLog + * @see PageVo + * @see SysLogVo + */ + fun sysLogPageToSysLogPageVo(syslogPage: IPage) = PageVo( + syslogPage.total, + syslogPage.pages, + syslogPage.size, + syslogPage.current, + syslogPage.records.map { sysLog -> + SysLogVo( + id = sysLog.id, + logType = sysLog.logType, + operateUserId = sysLog.operateUserId, + operateTime = sysLog.operateTime, + requestUri = sysLog.requestUri, + requestMethod = sysLog.requestMethod, + requestParams = sysLog.requestParams, + requestIp = sysLog.requestIp, + requestServerAddress = sysLog.requestServerAddress, + exception = sysLog.exception?.let { it == 1}, + exceptionInfo = sysLog.exceptionInfo, + startTime = sysLog.startTime, + endTime = sysLog.endTime, + executeTime = sysLog.executeTime, + userAgent = sysLog.userAgent, + operateUsername = sysLog.operateUsername + ) + }) + +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolBaseConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolBaseConverter.kt new file mode 100644 index 0000000..fb76be6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolBaseConverter.kt @@ -0,0 +1,77 @@ +package top.fatweb.oxygen.api.converter.tool + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.oxygen.api.entity.tool.ToolBase +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolBaseVo +import top.fatweb.oxygen.api.vo.tool.ToolDataVo + +/** + * Tool base converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ToolBaseConverter { + /** + * Convert ToolBase object into ToolBaseVo object + * + * @param toolBase ToolBase object + * @return ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBase + * @see ToolBaseVo + */ + fun toolBaseToToolBaseVo(toolBase: ToolBase) = ToolBaseVo( + id = toolBase.id, + name = toolBase.name, + source = toolBase.source?.let(ToolDataConverter::toolDataToToolDataVo), + dist = toolBase.dist?.let(ToolDataConverter::toolDataToToolDataVo), + platform = toolBase.platform, + compiled = toolBase.compiled?.let { it == 1}, + createTime = toolBase.createTime, + updateTime = toolBase.updateTime + ) + + /** + * Convert ToolBase object into ToolBaseVo object by get page + * + * @param toolBase ToolBase object + * @return ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBase + * @see ToolBaseVo + */ + fun toolBaseToToolBaseVoByGetList(toolBase: ToolBase) = ToolBaseVo( + id = toolBase.id, + name = toolBase.name, + source = ToolDataVo(id = toolBase.sourceId, data = null, createTime = null, updateTime = null), + dist = ToolDataVo(id = toolBase.distId, data = null, createTime = null, updateTime = null), + platform = toolBase.platform, + compiled = toolBase.compiled?.let { it == 1}, + createTime = toolBase.createTime, + updateTime = toolBase.updateTime + ) + + /** + * Convert IPage object into PageVo object + * + * @param toolBasePage IPage object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see ToolBase + * @see PageVo + * @see ToolBaseVo + */ + fun toolBasePageToToolBasePageVo(toolBasePage: IPage) = PageVo( + total = toolBasePage.total, + pages = toolBasePage.pages, + size = toolBasePage.size, + current = toolBasePage.current, + records = toolBasePage.records.map(::toolBaseToToolBaseVoByGetList) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolCategoryConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolCategoryConverter.kt new file mode 100644 index 0000000..9efcb6f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolCategoryConverter.kt @@ -0,0 +1,63 @@ +package top.fatweb.oxygen.api.converter.tool + +import top.fatweb.oxygen.api.entity.tool.ToolCategory +import top.fatweb.oxygen.api.param.tool.ToolCategoryAddParam +import top.fatweb.oxygen.api.param.tool.ToolCategoryUpdateParam +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo + +/** + * Tool category converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ToolCategoryConverter { + /** + * Convert ToolCategory object into ToolCategoryVo object + * + * @param toolCategory ToolCategory object + * @return ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategory + * @see ToolCategoryVo + */ + fun toolCategoryToToolCategoryVo(toolCategory: ToolCategory) = ToolCategoryVo( + id = toolCategory.id, + name = toolCategory.name, + enable = toolCategory.enable?.let { it == 1}, + createTime = toolCategory.createTime, + updateTime = toolCategory.updateTime + ) + + /** + * Convert ToolCategoryAddParam object into ToolCategory object + * + * @param toolCategoryAddParam ToolCategoryAddParam object + * @return ToolCateGory object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryAddParam + * @see ToolCategory + */ + fun toolCategoryAddParamToToolCategory(toolCategoryAddParam: ToolCategoryAddParam) = ToolCategory().apply { + name = toolCategoryAddParam.name + enable = if (toolCategoryAddParam.enable) 1 else 0 + } + + /** + * Convert ToolCategoryUpdateParam object into ToolCategory object + * + * @param toolCategoryUpdateParam ToolCategoryUpdateParam object + * @return ToolCategory object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryUpdateParam + * @see ToolCategory + */ + fun toolCategoryUpdateParamToToolCategory(toolCategoryUpdateParam: ToolCategoryUpdateParam) = ToolCategory().apply { + id = toolCategoryUpdateParam.id + name = toolCategoryUpdateParam.name + enable = toolCategoryUpdateParam.enable?. let { if (it) 1 else 0 } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolConverter.kt new file mode 100644 index 0000000..249b69d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolConverter.kt @@ -0,0 +1,67 @@ +package top.fatweb.oxygen.api.converter.tool + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import top.fatweb.oxygen.api.converter.permission.UserConverter +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ToolConverter { + /** + * Convert Tool object into ToolVo object + * + * @param tool Tool object + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + * @see ToolVo + */ + fun toolToToolVo(tool: Tool) = ToolVo( + id = tool.id, + name = tool.name, + toolId = tool.toolId, + icon = tool.icon, + platform = tool.platform, + description = tool.description, + base = tool.base?.let(ToolBaseConverter::toolBaseToToolBaseVo), + author = tool.author?.let(UserConverter::userToUserWithInfoVo), + ver = tool.ver, + keywords = tool.keywords, + categories = tool.categories?.map(ToolCategoryConverter::toolCategoryToToolCategoryVo), + source = tool.source?.let(ToolDataConverter::toolDataToToolDataVo), + dist = tool.dist?.let(ToolDataConverter::toolDataToToolDataVo), + entryPoint = tool.entryPoint, + publish = tool.publish, + review = tool.review, + createTime = tool.createTime, + updateTime = tool.updateTime, + favorite = tool.favorite?.let { it == 1} + ) + + /** + * Convert Page object into PageVo object + * + * @param toolPage Page object + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Page + * @see Tool + * @see PageVo + * @see ToolVo + */ + fun toolPageToToolPageVo(toolPage: Page): PageVo = PageVo( + total = toolPage.total, + pages = toolPage.pages, + size = toolPage.size, + current = toolPage.current, + records = toolPage.records.map(::toolToToolVo) + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolDataConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolDataConverter.kt new file mode 100644 index 0000000..117752d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolDataConverter.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.converter.tool + +import top.fatweb.oxygen.api.entity.tool.ToolData +import top.fatweb.oxygen.api.vo.tool.ToolDataVo + +/** + * Tool data converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ToolDataConverter { + /** + * Convert ToolData object into ToolDataVo object + * + * @param toolData ToolData object + * @return ToolDataVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolData + * @see ToolDataVo + */ + fun toolDataToToolDataVo(toolData: ToolData) = ToolDataVo( + id = toolData.id, + data = toolData.data, + createTime = toolData.createTime, + updateTime = toolData.updateTime + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolTemplateConverter.kt b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolTemplateConverter.kt new file mode 100644 index 0000000..7fb5663 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/converter/tool/ToolTemplateConverter.kt @@ -0,0 +1,106 @@ +package top.fatweb.oxygen.api.converter.tool + +import com.baomidou.mybatisplus.core.metadata.IPage +import top.fatweb.oxygen.api.entity.tool.ToolTemplate +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolBaseVo +import top.fatweb.oxygen.api.vo.tool.ToolDataVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo + +/** + * Tool template converter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ToolTemplateConverter { + /** + * Convert ToolTemplate object into ToolTemplateVo object + * + * @param toolTemplate ToolTemplate object + * @return ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplate + * @see ToolTemplateVo + */ + fun toolTemplateToToolTemplateVo(toolTemplate: ToolTemplate) = ToolTemplateVo( + id = toolTemplate.id, + name = toolTemplate.name, + base = toolTemplate.base?.let(ToolBaseConverter::toolBaseToToolBaseVo), + source = toolTemplate.source?.let(ToolDataConverter::toolDataToToolDataVo), + platform = toolTemplate.platform, + entryPoint = toolTemplate.entryPoint, + enable = toolTemplate.enable?.let { it == 1}, + createTime = toolTemplate.createTime, + updateTime = toolTemplate.updateTime + ) + + /** + * Convert IPage object into PageVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun toolTemplatePageToToolTemplatePageVo(toolTemplatePage: IPage) = PageVo( + total = toolTemplatePage.total, + pages = toolTemplatePage.pages, + size = toolTemplatePage.size, + current = toolTemplatePage.current, + records = toolTemplatePage.records.map(::toolTemplateToToolTemplateVo) + ) + + /** + * Convert ToolTemplate object into ToolTemplateVo object by list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun toolTemplateToToolTemplateVoByList(toolTemplate: ToolTemplate) = ToolTemplateVo( + id = toolTemplate.id, + name = toolTemplate.name, + base = ToolBaseVo( + id = toolTemplate.baseId, + name = null, + source = null, + dist = null, + platform = toolTemplate.base?.platform, + compiled = null, + createTime = null, + updateTime = null + ), + source = ToolDataVo(id = toolTemplate.sourceId, data = null, createTime = null, updateTime = null), + platform = toolTemplate.platform, + entryPoint = toolTemplate.entryPoint, + enable = toolTemplate.enable?.let { it == 1}, + createTime = toolTemplate.createTime, + updateTime = toolTemplate.updateTime + ) + + /** + * Convert ToolTemplate object into ToolTemplateVo object with base dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun toolTemplateToToolTemplateVoWithBaseDist(toolTemplate: ToolTemplate) = ToolTemplateVo( + id = toolTemplate.id, + name = toolTemplate.name, + base = ToolBaseVo( + id = toolTemplate.baseId, + name = toolTemplate.base?.name, + source = null, + dist = ToolDataVo(id = null, data = toolTemplate.base?.distData, createTime = null, updateTime = null), + platform = toolTemplate.base?.platform, + compiled = null, + createTime = null, + updateTime = null + ), + source = toolTemplate.source?.let(ToolDataConverter::toolDataToToolDataVo), + platform = toolTemplate.platform, + entryPoint = toolTemplate.entryPoint, + enable = toolTemplate.enable?.let { it == 1}, + createTime = toolTemplate.createTime, + updateTime = toolTemplate.updateTime + ) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/cron/StatisticsCron.kt b/src/main/kotlin/top/fatweb/oxygen/api/cron/StatisticsCron.kt new file mode 100644 index 0000000..e4b014f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/cron/StatisticsCron.kt @@ -0,0 +1,35 @@ +package top.fatweb.oxygen.api.cron + +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component +import top.fatweb.oxygen.api.entity.system.StatisticsLog +import top.fatweb.oxygen.api.properties.SecurityProperties +import top.fatweb.oxygen.api.service.system.IStatisticsLogService +import top.fatweb.oxygen.api.util.RedisUtil + +/** + * Statistics scheduled tasks + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Component +class StatisticsCron( + private val redisUtil: RedisUtil, + private val statisticsLogService: IStatisticsLogService +) { + /** + * Auto record number of online users + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Scheduled(cron = "0 * * * * *") + fun onlineUserCount() { + statisticsLogService.save(StatisticsLog().apply { + key = StatisticsLog.KeyItem.ONLINE_USERS_COUNT + value = redisUtil.keys("${SecurityProperties.jwtIssuer}_login_*") + .distinctBy { Regex("${SecurityProperties.jwtIssuer}_login_(.*):.*").matchEntire(it)?.groupValues?.getOrNull(1) }.size.toString() + }) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/common/BusinessCode.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/BusinessCode.kt new file mode 100644 index 0000000..5b1f363 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/BusinessCode.kt @@ -0,0 +1,49 @@ +package top.fatweb.oxygen.api.entity.common + +/** + * Business code entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +enum class BusinessCode(val code: Int) { + /** + * System + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + SYSTEM(100), + + /** + * Permission + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + PERMISSION(200), + + /** + * Database + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + DATABASE(300), + + /** + * Tool + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + TOOL(400), + + /** + * Avatar API + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + API_AVATAR(501) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseCode.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseCode.kt new file mode 100644 index 0000000..c95a4a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseCode.kt @@ -0,0 +1,81 @@ +package top.fatweb.oxygen.api.entity.common + +/** + * Response code entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +enum class ResponseCode(val code: Int) { + SYSTEM_OK(BusinessCode.SYSTEM, 0), + + SYSTEM_ERROR(BusinessCode.SYSTEM, 50), + SYSTEM_TIMEOUT(BusinessCode.SYSTEM, 51), + SYSTEM_REQUEST_ILLEGAL(BusinessCode.SYSTEM, 52), + SYSTEM_ARGUMENT_NOT_VALID(BusinessCode.SYSTEM, 53), + SYSTEM_INVALID_CAPTCHA_CODE(BusinessCode.SYSTEM, 54), + SYSTEM_REQUEST_TOO_FREQUENT(BusinessCode.SYSTEM, 55), + SYSTEM_MATCH_SENSITIVE_WORD(BusinessCode.SYSTEM, 56), + + PERMISSION_LOGIN_SUCCESS(BusinessCode.PERMISSION, 0), + PERMISSION_PASSWORD_CHANGE_SUCCESS(BusinessCode.PERMISSION, 1), + PERMISSION_LOGOUT_SUCCESS(BusinessCode.PERMISSION, 2), + PERMISSION_TOKEN_RENEW_SUCCESS(BusinessCode.PERMISSION, 3), + PERMISSION_REGISTER_SUCCESS(BusinessCode.PERMISSION, 4), + PERMISSION_RESEND_SUCCESS(BusinessCode.PERMISSION, 5), + PERMISSION_VERIFY_SUCCESS(BusinessCode.PERMISSION, 6), + PERMISSION_FORGET_SUCCESS(BusinessCode.PERMISSION, 7), + PERMISSION_RETRIEVE_SUCCESS(BusinessCode.PERMISSION, 8), + + PERMISSION_UNAUTHORIZED(BusinessCode.PERMISSION, 50), + PERMISSION_USERNAME_NOT_FOUND(BusinessCode.PERMISSION, 51), + PERMISSION_ACCESS_DENIED(BusinessCode.PERMISSION, 52), + PERMISSION_USER_LOCKED(BusinessCode.PERMISSION, 53), + PERMISSION_USER_EXPIRED(BusinessCode.PERMISSION, 54), + PERMISSION_USER_CREDENTIALS_EXPIRED(BusinessCode.PERMISSION, 55), + PERMISSION_USER_DISABLE(BusinessCode.PERMISSION, 56), + PERMISSION_LOGIN_USERNAME_PASSWORD_ERROR(BusinessCode.PERMISSION, 57), + PERMISSION_OLD_PASSWORD_NOT_MATCH(BusinessCode.PERMISSION, 58), + PERMISSION_LOGOUT_FAILED(BusinessCode.PERMISSION, 59), + PERMISSION_TOKEN_ILLEGAL(BusinessCode.PERMISSION, 60), + PERMISSION_TOKEN_HAS_EXPIRED(BusinessCode.PERMISSION, 61), + PERMISSION_NO_VERIFICATION_REQUIRED(BusinessCode.PERMISSION, 62), + PERMISSION_VERIFY_CODE_ERROR_OR_EXPIRED(BusinessCode.PERMISSION, 63), + PERMISSION_ACCOUNT_NEED_INIT(BusinessCode.PERMISSION, 64), + PERMISSION_USER_NOT_FOUND(BusinessCode.PERMISSION, 65), + PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED(BusinessCode.PERMISSION, 66), + PERMISSION_ACCOUNT_NEED_RESET_PASSWORD(BusinessCode.PERMISSION, 67), + PERMISSION_NEED_TWO_FACTOR(BusinessCode.PERMISSION, 68), + PERMISSION_ALREADY_HAS_TWO_FACTOR(BusinessCode.PERMISSION, 69), + PERMISSION_NO_TWO_FACTOR_FOUND(BusinessCode.PERMISSION, 70), + PERMISSION_TWO_FACTOR_VERIFICATION_CODE_ERROR(BusinessCode.PERMISSION, 71), + + DATABASE_SELECT_SUCCESS(BusinessCode.DATABASE, 0), + DATABASE_SELECT_FAILED(BusinessCode.DATABASE, 5), + DATABASE_INSERT_SUCCESS(BusinessCode.DATABASE, 10), + DATABASE_INSERT_FAILED(BusinessCode.DATABASE, 15), + DATABASE_UPDATE_SUCCESS(BusinessCode.DATABASE, 20), + DATABASE_UPDATE_FAILED(BusinessCode.DATABASE, 25), + DATABASE_DELETE_SUCCESS(BusinessCode.DATABASE, 30), + DATABASE_DELETE_FAILED(BusinessCode.DATABASE, 35), + DATABASE_EXECUTE_ERROR(BusinessCode.DATABASE, 50), + DATABASE_DUPLICATE_KEY(BusinessCode.DATABASE, 51), + DATABASE_NO_RECORD_FOUND(BusinessCode.DATABASE, 52), + DATABASE_RECORD_ALREADY_EXISTS(BusinessCode.DATABASE, 53), + + TOOL_SUBMIT_SUCCESS(BusinessCode.TOOL, 10), + TOOL_CANCEL_SUCCESS(BusinessCode.TOOL, 11), + TOOL_ILLEGAL_VERSION(BusinessCode.TOOL, 50), + TOOL_UNDER_REVIEW(BusinessCode.TOOL, 51), + TOOL_NOT_UNDER_REVIEW(BusinessCode.TOOL, 52), + TOOL_HAS_UNPUBLISHED_VERSION(BusinessCode.TOOL, 53), + TOOL_HAS_NOT_BEEN_PUBLISHED(BusinessCode.TOOL, 54), + TOOL_HAS_BEEN_PUBLISHED(BusinessCode.TOOL, 55), + TOOL_SUBMIT_ERROR(BusinessCode.TOOL, 60), + TOOL_CANCEL_ERROR(BusinessCode.TOOL, 61), + + API_AVATAR_SUCCESS(BusinessCode.API_AVATAR, 0), + API_AVATAR_ERROR(BusinessCode.API_AVATAR, 50); + + constructor(businessCode: BusinessCode, code: Int) : this(businessCode.code * 100 + code) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseResult.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseResult.kt new file mode 100644 index 0000000..7c866fe --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/common/ResponseResult.kt @@ -0,0 +1,103 @@ +package top.fatweb.oxygen.api.entity.common + +import io.swagger.v3.oas.annotations.media.Schema +import java.io.Serializable + +/** + * Response result entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +class ResponseResult private constructor( + @Schema(description = "响应码", defaultValue = "200") val code: Int, + + @Schema(description = "是否调用成功") val success: Boolean, + + @Schema(description = "信息") val msg: String, + + @Schema(description = "数据") val data: T? +) : Serializable { + companion object { + /** + * Build response result object + * + * @param code Response code + * @param success Is successful + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun build(code: Int, success: Boolean, msg: String, data: T?) = + ResponseResult(code, success, msg, data) + + /** + * Build response result object + * + * @param code Response code object + * @param success Is successful + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseCode + */ + fun build(code: ResponseCode, success: Boolean, msg: String, data: T?) = + build(code.code, success, msg, data) + + /** + * Build successful response result object + * + * @param code Response code object + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseCode + */ + fun success(code: ResponseCode = ResponseCode.SYSTEM_OK, msg: String = "success", data: T? = null) = + build(code, true, msg, data) + + /** + * Build failure response result object + * + * @param code Response code object + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseCode + */ + fun fail(code: ResponseCode = ResponseCode.SYSTEM_ERROR, msg: String = "fail", data: T? = null) = + build(code, false, msg, data) + + /** + * Build database successful response result object + * + * @param code Response code object + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseCode + */ + fun databaseSuccess( + code: ResponseCode = ResponseCode.DATABASE_SELECT_SUCCESS, msg: String = "success", data: T? = null + ) = build(code, true, msg, data) + + /** + * Build database failure response result object + * + * @param code Response code object + * @param msg Response message + * @param data Response data + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ResponseCode + */ + fun databaseFail( + code: ResponseCode = ResponseCode.DATABASE_SELECT_FAILED, msg: String = "fail", data: T? = null + ) = build(code, false, msg, data) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Func.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Func.kt new file mode 100644 index 0000000..61fa701 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Func.kt @@ -0,0 +1,55 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Function entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_func") +class Func : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Parent ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("parent_id") + var parentId: Long? = null + + /** + * Menu ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("menu_id") + var menuId: Long? = null + + override fun toString(): String { + return "Func(id=$id, name=$name, parentId=$parentId, menuId=$menuId)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Group.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Group.kt new file mode 100644 index 0000000..5d779ad --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Group.kt @@ -0,0 +1,95 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Group entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_group") +class Group : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * Role list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + */ + @TableField(exist = false) + var roles: List? = null + + override fun toString(): String { + return "Group(id=$id, name=$name, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, roles=$roles)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/LoginUser.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/LoginUser.kt new file mode 100644 index 0000000..29fafce --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/LoginUser.kt @@ -0,0 +1,67 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.fasterxml.jackson.annotation.JsonIgnore +import com.fasterxml.jackson.annotation.JsonTypeInfo +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.userdetails.UserDetails +import java.time.LocalDateTime +import java.time.ZoneOffset + +/** + * Login user entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserDetails + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +class LoginUser() : UserDetails { + /** + * User object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + lateinit var user: User + + @JsonIgnore + private var authorities: List? = null + + constructor(user: User) : this() { + this.user = user + } + + @JsonIgnore + override fun getAuthorities(): List { + authorities?.let { return it } + authorities = user.operations?.map { SimpleGrantedAuthority(it.code) } ?: emptyList() + + return authorities as List + } + + @JsonIgnore + override fun getPassword() = user.password + + @JsonIgnore + override fun getUsername() = user.username + + @JsonIgnore + override fun isAccountNonExpired() = + user.expiration == null || user.expiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC)) + + @JsonIgnore + override fun isAccountNonLocked() = user.locking == 0 + + @JsonIgnore + override fun isCredentialsNonExpired() = + user.credentialsExpiration == null || user.credentialsExpiration!!.isAfter(LocalDateTime.now(ZoneOffset.UTC)) + + @JsonIgnore + override fun isEnabled() = user.enable == 1 + + override fun toString(): String { + return "LoginUser(user=$user, authorities=$authorities)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Menu.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Menu.kt new file mode 100644 index 0000000..d81e235 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Menu.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Menu entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_menu") +class Menu : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("url") + var url: String? = null + + /** + * Parent ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("parent_id") + var parentId: Long? = null + + /** + * Module ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("module_id") + var moduleId: Long? = null + + override fun toString(): String { + return "Menu(id=$id, name=$name, url=$url, parentId=$parentId, moduleId=$moduleId)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Module.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Module.kt new file mode 100644 index 0000000..fa26a28 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Module.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Module Entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_module") +class Module : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + override fun toString(): String { + return "Module(id=$id, name=$name)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Operation.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Operation.kt new file mode 100644 index 0000000..c111ce6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Operation.kt @@ -0,0 +1,55 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Operation entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_operation") +class Operation : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("code") + var code: String? = null + + /** + * Function ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("func_id") + var funcId: Long? = null + + override fun toString(): String { + return "Operation(id=$id, name=$name, code=$code, funcId=$funcId)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Power.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Power.kt new file mode 100644 index 0000000..4f1a7a0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Power.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Power entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_power") +class Power : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Type ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("type_id") + var typeId: Int? = null + + override fun toString(): String { + return "Power(id=$id, typeId=$typeId)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerSet.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerSet.kt new file mode 100644 index 0000000..c99f5ae --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerSet.kt @@ -0,0 +1,51 @@ +package top.fatweb.oxygen.api.entity.permission + +import java.io.Serializable + +/** + * Set of power entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +class PowerSet : Serializable { + /** + * Module list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Module + */ + var moduleList: List? = null + + /** + * Menu list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Menu + */ + var menuList: List? = null + + /** + * Function list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Func + */ + var funcList: List? = null + + /** + * Operation list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Operation + */ + var operationList: List? = null + + override fun toString(): String { + return "PowerSet(moduleList=$moduleList, menuList=$menuList, funcList=$funcList, operationList=$operationList)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerType.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerType.kt new file mode 100644 index 0000000..c2be1e4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/PowerType.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import java.io.Serializable + +/** + * Power type entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_power_type") +class PowerType : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + override fun toString(): String { + return "PowerType(id=$id, name=$name)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RPowerRole.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RPowerRole.kt new file mode 100644 index 0000000..fc0da02 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RPowerRole.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +/** + * Power role intermediate entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_r_power_role") +class RPowerRole : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Power ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("power_id") + var powerId: Long? = null + + /** + * Role ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("role_id") + var roleId: Long? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "RPowerRole(id=$id, powerId=$powerId, roleId=$roleId, deleted=$deleted, version=$version)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RRoleGroup.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RRoleGroup.kt new file mode 100644 index 0000000..a3af844 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RRoleGroup.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +/** + * Role group intermediate entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_r_role_group") +class RRoleGroup : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Role ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("role_id") + var roleId: Long? = null + + /** + * Group ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("group_id") + var groupId: Long? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "RRoleGroup(id=$id, roleId=$roleId, groupId=$groupId, deleted=$deleted, version=$version)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserGroup.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserGroup.kt new file mode 100644 index 0000000..60986f3 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserGroup.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +/** + * User group intermediate entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_r_user_group") +class RUserGroup : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("user_id") + var userId: Long? = null + + /** + * Group ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("group_id") + var groupId: Long? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "RUserGroup(id=$id, userId=$userId, groupId=$groupId, deleted=$deleted, version=$version)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserRole.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserRole.kt new file mode 100644 index 0000000..a3f7d77 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/RUserRole.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +/** + * User role intermediate entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_r_user_role") +class RUserRole : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("user_id") + var userId: Long? = null + + /** + * Role ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("role_id") + var roleId: Long? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "RUserRole(id=$id, userId=$userId, roleId=$roleId, deleted=$deleted, version=$version)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Role.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Role.kt new file mode 100644 index 0000000..e5175fc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/Role.kt @@ -0,0 +1,135 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Role entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_role") +class Role : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * Module list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Module + */ + @TableField(exist = false) + var modules: List? = null + + /** + * Menu list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Menu + */ + @TableField(exist = false) + var menus: List? = null + + /** + * Function list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Func + */ + @TableField(exist = false) + var funcs: List? = null + + /** + * Operation list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Operation + */ + @TableField(exist = false) + var operations: List? = null + + /** + * Power list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Power + */ + @TableField(exist = false) + var powers: List? = null + + override fun toString(): String { + return "Role(id=$id, name=$name, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, modules=$modules, menus=$menus, funcs=$funcs, operations=$operations, powers=$powers)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/User.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/User.kt new file mode 100644 index 0000000..93512fb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/User.kt @@ -0,0 +1,265 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * User entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_user") +class User() : Serializable { + constructor(id: Long?, username: String, password: String, enable: Boolean = true) : this() { + this.id = id + this.username = username + this.password = password + this.enable = if (enable) 1 else 0 + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("username") + var username: String? = null + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("password") + var password: String? = null + + /** + * Two-factor + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("two_factor") + var twoFactor: String? = null + + /** + * Verify email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("verify") + var verify: String? = null + + /** + * Forget password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("forget") + var forget: String? = null + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("locking") + var locking: Int? = null + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("expiration") + var expiration: LocalDateTime? = null + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("credentials_expiration") + var credentialsExpiration: LocalDateTime? = null + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("current_login_time") + var currentLoginTime: LocalDateTime? = null + + /** + * Current login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("current_login_ip") + var currentLoginIp: String? = null + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("last_login_time") + var lastLoginTime: LocalDateTime? = null + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("last_login_ip") + var lastLoginIp: String? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time") + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time") + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * User information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfo + */ + @TableField(exist = false) + var userInfo: UserInfo? = null + + /** + * Role list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + */ + @TableField(exist = false) + var roles: List? = null + + /** + * Group list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Group + */ + @TableField(exist = false) + var groups: List? = null + + /** + * Module list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Module + */ + @TableField(exist = false) + var modules: List? = null + + /** + * Menu list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Menu + */ + @TableField(exist = false) + var menus: List? = null + + /** + * Function list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Func + */ + @TableField(exist = false) + var funcs: List? = null + + /** + * Operation list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Operation + */ + @TableField(exist = false) + var operations: List? = null + + override fun toString(): String { + return "User(id=$id, username=$username, password=$password, twoFactor=$twoFactor, verify=$verify, forget=$forget, locking=$locking, expiration=$expiration, credentialsExpiration=$credentialsExpiration, enable=$enable, currentLoginTime=$currentLoginTime, currentLoginIp=$currentLoginIp, lastLoginTime=$lastLoginTime, lastLoginIp=$lastLoginIp, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, userInfo=$userInfo, roles=$roles, groups=$groups, modules=$modules, menus=$menus, funcs=$funcs, operations=$operations)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/UserInfo.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/UserInfo.kt new file mode 100644 index 0000000..8aa8e24 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/permission/UserInfo.kt @@ -0,0 +1,103 @@ +package top.fatweb.oxygen.api.entity.permission + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * User information entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_user_info") +class UserInfo : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("user_id") + var userId: Long? = null + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("nickname") + var nickname: String? = null + + /** + * Avatar in base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("avatar") + var avatar: String? = null + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("email") + var email: String? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "UserInfo(id=$id, userId=$userId, nickname=$nickname, avatar=$avatar, email=$email, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/system/EventLog.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/EventLog.kt new file mode 100644 index 0000000..c689547 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/EventLog.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonValue +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Event log entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_l_event_log") +class EventLog : Serializable { + enum class Event(@field:EnumValue @field:JsonValue val code: String) { + LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), VERIFY("VERIFY"), API("API") + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Event + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("event") + var event: Event? = null + + /** + * Operate user ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("operate_user_id") + var operateUserId: Long? = null + + /** + * Operate time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") + @TableField("operate_time") + var operateTime: LocalDateTime? = null + + override fun toString(): String { + return "EventLog(id=$id, event=$event, operateUserId=$operateUserId, operateTime=$operateTime)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SensitiveWord.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SensitiveWord.kt new file mode 100644 index 0000000..a024d6e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SensitiveWord.kt @@ -0,0 +1,63 @@ +package top.fatweb.oxygen.api.entity.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler +import com.fasterxml.jackson.annotation.JsonValue +import java.io.Serializable + +/** + * Sensitive word entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_s_sensitive_word", autoResultMap = true) +class SensitiveWord : Serializable { + enum class Use(@field:EnumValue @field:JsonValue val code: String) { + USERNAME("USERNAME"), TITLE("TITLE"); + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Word + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("word") + var word: String? = null + + /** + * Use for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(value = "use_for", typeHandler = JacksonTypeHandler::class) + @JvmField + var useFor: Set? = null + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + override fun toString(): String { + return "SensitiveWord(id=$id, word=$word, useFor=$useFor, enable=$enable)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/system/StatisticsLog.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/StatisticsLog.kt new file mode 100644 index 0000000..165a2c4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/StatisticsLog.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonValue +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Statistics log entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_l_statistics_log") +class StatisticsLog : Serializable { + enum class KeyItem(@field:EnumValue @field:JsonValue val code: String) { + ONLINE_USERS_COUNT("ONLINE_USER_COUNT") + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Key + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("key") + var key: KeyItem? = null + + /** + * Value + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("value") + var value: String? = null + + /** + * Record time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") + @TableField("record_time") + var recordTime: LocalDateTime? = null + + override fun toString(): String { + return "StatisticsLog(id=$id, key=$key, value=$value, recordTime=$recordTime)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SysLog.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SysLog.kt new file mode 100644 index 0000000..b17e2fa --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/system/SysLog.kt @@ -0,0 +1,186 @@ +package top.fatweb.oxygen.api.entity.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.baomidou.mybatisplus.annotation.TableField +import com.baomidou.mybatisplus.annotation.TableId +import com.baomidou.mybatisplus.annotation.TableName +import com.fasterxml.jackson.annotation.JsonFormat +import com.fasterxml.jackson.annotation.JsonValue +import java.io.Serializable +import java.time.LocalDateTime + +/** + * System log entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_l_sys_log") +class SysLog : Serializable { + /** + * Log type enum + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + enum class LogType(@field:EnumValue @field:JsonValue val code: String) { + INFO("INFO"), ERROR("ERROR"), LOGIN("LOGIN"), LOGOUT("LOGOUT"), REGISTER("REGISTER"), STATISTICS("STATISTICS"), API( + "API" + ) + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Log type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LogType + */ + @TableField("log_type") + var logType: LogType? = null + + /** + * Operate user ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("operate_user_id") + var operateUserId: Long? = null + + /** + * Operate time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") + @TableField("operate_time") + var operateTime: LocalDateTime? = null + + /** + * Request URI + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("request_uri") + var requestUri: String? = null + + /** + * Request method + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("request_method") + var requestMethod: String? = null + + /** + * Request parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("request_params") + var requestParams: String? = null + + /** + * Request IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("request_ip") + var requestIp: String? = null + + /** + * Request server address + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("request_server_address") + var requestServerAddress: String? = null + + /** + * Is exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("exception") + var exception: Int? = null + + /** + * Exception information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("exception_info") + var exceptionInfo: String? = null + + /** + * Start time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") + @TableField("start_time") + var startTime: LocalDateTime? = null + + /** + * End time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS") + @TableField("end_time") + var endTime: LocalDateTime? = null + + /** + * Execute time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("execute_time") + var executeTime: Long? = null + + /** + * User agent + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("user_agent") + var userAgent: String? = null + + /** + * Operate username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var operateUsername: String? = null + + override fun toString(): String { + return "SysLog(id=$id, logType=$logType, operateUserId=$operateUserId, operateTime=$operateTime, requestUri=$requestUri, requestMethod=$requestMethod, requestParams=$requestParams, requestIp=$requestIp, requestServerAddress=$requestServerAddress, exception=$exception, exceptionInfo=$exceptionInfo, startTime=$startTime, endTime=$endTime, executeTime=$executeTime, userAgent=$userAgent, operateUsername=$operateUsername)" + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Platform.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Platform.kt new file mode 100644 index 0000000..f959266 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Platform.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.fasterxml.jackson.annotation.JsonValue + +/** + * Platform enum + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +enum class Platform(@field:EnumValue @field:JsonValue val code: String) { + WEB("WEB"), DESKTOP("DESKTOP"), ANDROID("ANDROID") +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/RToolCategory.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/RToolCategory.kt new file mode 100644 index 0000000..cce2436 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/RToolCategory.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +/** + * Tool category intermediate entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_r_tool_main_category") +class RToolCategory : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("tool_id") + var toolId: Long? = null + + /** + * Category ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("category_id") + var categoryId: Long? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "RToolCategory(id=$id, toolId=$toolId, categoryId=$categoryId, deleted=$deleted, version=$version)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Tool.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Tool.kt new file mode 100644 index 0000000..bef4466 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/Tool.kt @@ -0,0 +1,262 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler +import com.fasterxml.jackson.annotation.JsonValue +import top.fatweb.oxygen.api.entity.permission.User +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Tool entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_b_tool_main", autoResultMap = true) +class Tool : Serializable { + /** + * Tool review type enum + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + enum class ReviewType(@field:EnumValue @field:JsonValue val code: String) { + NONE("NONE"), PROCESSING("PROCESSING"), PASS("PASS"), REJECT("REJECT") + } + + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("tool_id") + var toolId: String? = null + + /** + * Icon + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("icon") + var icon: String? = null + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @TableField("platform") + var platform: Platform? = null + + /** + * Description + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("description") + var description: String? = null + + /** + * Base ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("base_id") + var baseId: Long? = null + + /** + * Author ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("author_id") + var authorId: Long? = null + + /** + * Version of tool + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("ver") + var ver: String? = null + + /** + * Keywords + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("keywords", typeHandler = JacksonTypeHandler::class) + var keywords: List? = null + + /** + * Source code ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("source_id") + var sourceId: Long? = null + + /** + * Compile product ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("dist_id") + var distId: Long? = null + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("entry_point") + var entryPoint: String? = null + + /** + * Publish + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("publish") + var publish: Long? = null + + /** + * Review + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ReviewType + */ + @TableField("review") + var review: ReviewType? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * Author + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var author: User? = null + + /** + * Base + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var base: ToolBase? = null + + /** + * Categories + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var categories: List? = null + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var source: ToolData? = null + + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var dist: ToolData? = null + + /** + * Favorite + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var favorite: Int? = null + + override fun toString(): String { + return "Tool(id=$id, name=$name, toolId=$toolId, icon=$icon, platform=$platform, description=$description, baseId=$baseId, authorId=$authorId, ver=$ver, keywords=$keywords, sourceId=$sourceId, distId=$distId, entryPoint=$entryPoint, publish=$publish, review=$review, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, author=$author, base=$base, categories=$categories, source=$source, dist=$dist, favorite=$favorite)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolBase.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolBase.kt new file mode 100644 index 0000000..a88745b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolBase.kt @@ -0,0 +1,140 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Tool base entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_b_tool_base") +class ToolBase : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Source ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("source_id") + var sourceId: Long? = null + + /** + * Dist ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("dist_id") + var distId: Long? = null + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @TableField("platform") + var platform: Platform? = null + + /** + * Has compiled + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("compiled") + var compiled: Int? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var source: ToolData? = null + + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var dist: ToolData? = null + + /** + * Dist data + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var distData: String? = null + + override fun toString(): String { + return "ToolBase(id=$id, name=$name, sourceId=$sourceId, distId=$distId, platform=$platform, compiled=$compiled, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, source=$source, dist=$dist, distData=$distData)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolCategory.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolCategory.kt new file mode 100644 index 0000000..37246b1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolCategory.kt @@ -0,0 +1,85 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Tool category entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_b_tool_category") +class ToolCategory : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Enabel + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "ToolCategory(id=$id, name=$name, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolData.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolData.kt new file mode 100644 index 0000000..dd1f0dd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolData.kt @@ -0,0 +1,76 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Tool data entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_b_tool_data") +class ToolData : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Data + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("data") + var data: String? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "ToolData(id=$id, data=$data, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolFavorite.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolFavorite.kt new file mode 100644 index 0000000..1d474e6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolFavorite.kt @@ -0,0 +1,67 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable + +@TableName("t_b_tool_favorite") +class ToolFavorite : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("user_id") + var userId: Long? = null + + /** + * Author ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("author_id") + var authorId: Long? = null + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("tool_id") + var toolId: String? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + override fun toString(): String { + return "ToolFavorite(id=$id, userId=$userId, authorId=$authorId, toolId=$toolId, deleted=$deleted, version=$version)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolTemplate.kt b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolTemplate.kt new file mode 100644 index 0000000..b2b5007 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/entity/tool/ToolTemplate.kt @@ -0,0 +1,140 @@ +package top.fatweb.oxygen.api.entity.tool + +import com.baomidou.mybatisplus.annotation.* +import java.io.Serializable +import java.time.LocalDateTime + +/** + * Tool template entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@TableName("t_b_tool_template") +class ToolTemplate : Serializable { + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableId("id") + var id: Long? = null + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("name") + var name: String? = null + + /** + * Base ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("base_id") + var baseId: Long? = null + + /** + * Source ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("source_id") + var sourceId: Long? = null + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @TableField("platform") + var platform: Platform? = null + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("entry_point") + var entryPoint: String? = null + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("enable") + var enable: Int? = null + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("create_time", fill = FieldFill.INSERT) + var createTime: LocalDateTime? = null + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @TableField("update_time", fill = FieldFill.INSERT_UPDATE) + var updateTime: LocalDateTime? = null + + /** + * Deleted + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("deleted") + @TableLogic + var deleted: Long? = null + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField("version") + @Version + var version: Int? = null + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var source: ToolData? = null + + /** + * Base + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @TableField(exist = false) + var base: ToolBase? = null + + override fun toString(): String { + return "ToolTemplate(id=$id, name=$name, baseId=$baseId, sourceId=$sourceId, platform=$platform, entryPoint=$entryPoint, enable=$enable, createTime=$createTime, updateTime=$updateTime, deleted=$deleted, version=$version, source=$source, base=$base)" + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedInitException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedInitException.kt new file mode 100644 index 0000000..311a9a6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedInitException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Account need initialize exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class AccountNeedInitException : RuntimeException("Account need initialize") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedResetPasswordException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedResetPasswordException.kt new file mode 100644 index 0000000..756682b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/AccountNeedResetPasswordException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Account need reset password exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class AccountNeedResetPasswordException : RuntimeException("Account need reset password") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/AlreadyHasTwoFactorException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/AlreadyHasTwoFactorException.kt new file mode 100644 index 0000000..f9339cc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/AlreadyHasTwoFactorException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Already has two-factor exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class AlreadyHasTwoFactorException : RuntimeException("Already has two-factor") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseDeleteException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseDeleteException.kt new file mode 100644 index 0000000..cd1ea34 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseDeleteException.kt @@ -0,0 +1,11 @@ +package top.fatweb.oxygen.api.exception + +/** + * Database delete exception + * + * @param message Exception message + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class DatabaseDeleteException(message: String = "Database delete failed"): RuntimeException(message) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseInsertException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseInsertException.kt new file mode 100644 index 0000000..b3e301d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseInsertException.kt @@ -0,0 +1,11 @@ +package top.fatweb.oxygen.api.exception + +/** + * Database insert exception + * + * @param message Exception message + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class DatabaseInsertException(message: String = "Database insert failed"): RuntimeException(message) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseSelectException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseSelectException.kt new file mode 100644 index 0000000..1b5623b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseSelectException.kt @@ -0,0 +1,11 @@ +package top.fatweb.oxygen.api.exception + +/** + * Database select exception + * + * @param message Exception message + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class DatabaseSelectException(message: String = "Database select failed"): RuntimeException(message) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseUpdateException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseUpdateException.kt new file mode 100644 index 0000000..43f6c09 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/DatabaseUpdateException.kt @@ -0,0 +1,11 @@ +package top.fatweb.oxygen.api.exception + +/** + * Database update exception + * + * @param message Exception message + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class DatabaseUpdateException(message: String = "Database update failed"): RuntimeException(message) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/IllegalVersionException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/IllegalVersionException.kt new file mode 100644 index 0000000..b53fc99 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/IllegalVersionException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Illegal version exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class IllegalVersionException : RuntimeException("Illegal Version") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/InvalidCaptchaCodeException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/InvalidCaptchaCodeException.kt new file mode 100644 index 0000000..6e2b6a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/InvalidCaptchaCodeException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Invalid captcha code exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class InvalidCaptchaCodeException : RuntimeException("Invalid captcha code") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/MatchSensitiveWordException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/MatchSensitiveWordException.kt new file mode 100644 index 0000000..dbf5cf6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/MatchSensitiveWordException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Match sensitive word exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class MatchSensitiveWordException : RuntimeException("Match sensitive word") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/NeedTwoFactorException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/NeedTwoFactorException.kt new file mode 100644 index 0000000..fb51ca4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/NeedTwoFactorException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Need two-factor code exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class NeedTwoFactorException : RuntimeException("Need two-factor code") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/NoEmailConfigException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoEmailConfigException.kt new file mode 100644 index 0000000..a7bd6f2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoEmailConfigException.kt @@ -0,0 +1,13 @@ +package top.fatweb.oxygen.api.exception + +/** + * Email settings not configured exception + * + * @param configs Configs not config + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class NoEmailConfigException( + vararg configs: String +) : RuntimeException("Email settings not configured: ${configs.joinToString(", ")}") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/NoRecordFoundException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoRecordFoundException.kt new file mode 100644 index 0000000..65274fa --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoRecordFoundException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * No record found exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class NoRecordFoundException : RuntimeException("No record found") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/NoTwoFactorFoundException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoTwoFactorFoundException.kt new file mode 100644 index 0000000..c739da1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoTwoFactorFoundException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * No two-factor found exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class NoTwoFactorFoundException : RuntimeException("No two-factor found") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/NoVerificationRequiredException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoVerificationRequiredException.kt new file mode 100644 index 0000000..c0bcc3a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/NoVerificationRequiredException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * No verification required exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class NoVerificationRequiredException : RuntimeException("No verification required") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/RecordAlreadyExists.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/RecordAlreadyExists.kt new file mode 100644 index 0000000..20aaf88 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/RecordAlreadyExists.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Record already exists exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class RecordAlreadyExists : RuntimeException("Record already exists") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/RequestTooFrequentException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/RequestTooFrequentException.kt new file mode 100644 index 0000000..59a5b16 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/RequestTooFrequentException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Request too frequent exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class RequestTooFrequentException: RuntimeException("Request too frequent") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/RetrieveCodeErrorOrExpiredException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/RetrieveCodeErrorOrExpiredException.kt new file mode 100644 index 0000000..d8010c1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/RetrieveCodeErrorOrExpiredException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Retrieve code error or expired exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class RetrieveCodeErrorOrExpiredException : RuntimeException("Retrieve code error or expired") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/TokenHasExpiredException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/TokenHasExpiredException.kt new file mode 100644 index 0000000..7250ef7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/TokenHasExpiredException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Token has expired exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class TokenHasExpiredException : RuntimeException("Token has expired") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasBeenPublishedException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasBeenPublishedException.kt new file mode 100644 index 0000000..f80dfca --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasBeenPublishedException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Tool has been published exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class ToolHasBeenPublishedException : RuntimeException("Tool has been published") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasNotBeenPublishedException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasNotBeenPublishedException.kt new file mode 100644 index 0000000..af82607 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasNotBeenPublishedException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Tool has not been published exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class ToolHasNotBeenPublishedException : RuntimeException("Tool has not been published") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasUnpublishedVersionException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasUnpublishedVersionException.kt new file mode 100644 index 0000000..0b4d41e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolHasUnpublishedVersionException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Tool has unpublished version exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class ToolHasUnpublishedVersionException : RuntimeException("Has unpublished version") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolNotUnderReviewException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolNotUnderReviewException.kt new file mode 100644 index 0000000..586d71f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolNotUnderReviewException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Tool not under review exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class ToolNotUnderReviewException : RuntimeException("Tool not under review") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolUnderReviewException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolUnderReviewException.kt new file mode 100644 index 0000000..83083f0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/ToolUnderReviewException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Tool under review exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class ToolUnderReviewException : RuntimeException("Tool under review") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/TwoFactorVerificationCodeErrorException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/TwoFactorVerificationCodeErrorException.kt new file mode 100644 index 0000000..e93a68d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/TwoFactorVerificationCodeErrorException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Two-factor verification code error exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class TwoFactorVerificationCodeErrorException : RuntimeException("Two-factor verification code error") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/UserNotFoundException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/UserNotFoundException.kt new file mode 100644 index 0000000..6b2dc60 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/UserNotFoundException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * User not found exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class UserNotFoundException : RuntimeException("User not found") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/exception/VerificationCodeErrorOrExpiredException.kt b/src/main/kotlin/top/fatweb/oxygen/api/exception/VerificationCodeErrorOrExpiredException.kt new file mode 100644 index 0000000..bfbbee7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/exception/VerificationCodeErrorOrExpiredException.kt @@ -0,0 +1,10 @@ +package top.fatweb.oxygen.api.exception + +/** + * Verification code error or expired exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RuntimeException + */ +class VerificationCodeErrorOrExpiredException : RuntimeException("Verification code is error or has expired") \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/filter/ExceptionFilter.kt b/src/main/kotlin/top/fatweb/oxygen/api/filter/ExceptionFilter.kt new file mode 100644 index 0000000..6117074 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/filter/ExceptionFilter.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.api.filter + +import jakarta.servlet.Filter +import jakarta.servlet.FilterChain +import jakarta.servlet.ServletRequest +import jakarta.servlet.ServletResponse +import org.springframework.stereotype.Component + +/** + * Exception filter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Filter + */ +@Component +class ExceptionFilter : Filter { + override fun doFilter( + servletRequest: ServletRequest?, servletResponse: ServletResponse?, filterChain: FilterChain? + ) { + try { + filterChain!!.doFilter(servletRequest, servletResponse) + } catch (e: Exception) { + servletRequest?.setAttribute("filter.error", e) + servletRequest?.getRequestDispatcher("/error/thrown")?.forward(servletRequest, servletResponse) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/filter/JwtAuthenticationTokenFilter.kt b/src/main/kotlin/top/fatweb/oxygen/api/filter/JwtAuthenticationTokenFilter.kt new file mode 100644 index 0000000..a103133 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/filter/JwtAuthenticationTokenFilter.kt @@ -0,0 +1,57 @@ +package top.fatweb.oxygen.api.filter + +import jakarta.servlet.FilterChain +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.stereotype.Component +import org.springframework.util.StringUtils +import org.springframework.web.filter.OncePerRequestFilter +import top.fatweb.oxygen.api.entity.permission.LoginUser +import top.fatweb.oxygen.api.exception.TokenHasExpiredException +import top.fatweb.oxygen.api.properties.SecurityProperties +import top.fatweb.oxygen.api.util.JwtUtil +import top.fatweb.oxygen.api.util.RedisUtil +import top.fatweb.oxygen.api.util.WebUtil + +/** + * Jwt authentication token filter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RedisUtil + * @see OncePerRequestFilter + */ +@Component +class JwtAuthenticationTokenFilter(private val redisUtil: RedisUtil) : OncePerRequestFilter() { + override fun doFilterInternal( + request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain + ) { + val tokenWithPrefix = request.getHeader(SecurityProperties.headerKey) + + if (!StringUtils.hasText(tokenWithPrefix) || "/error/thrown" == request.servletPath) { + filterChain.doFilter(request, response) + return + } + + val token = WebUtil.getToken(tokenWithPrefix) + JwtUtil.parseJwt(token) + + val redisKeyPattern = "${SecurityProperties.jwtIssuer}_login_*:${token}" + val redisKeys = redisUtil.keys(redisKeyPattern) + if (redisKeys.isEmpty()) { + throw TokenHasExpiredException() + } + + val loginUser = redisUtil.getObject(redisKeys.first()) + loginUser ?: throw TokenHasExpiredException() + + redisUtil.setExpire(redisKeys.first(), SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit) + + val authenticationToken = UsernamePasswordAuthenticationToken(loginUser, null, loginUser.authorities) + SecurityContextHolder.getContext().authentication = authenticationToken + + filterChain.doFilter(request, response) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/handler/DataMetaObjectHandler.kt b/src/main/kotlin/top/fatweb/oxygen/api/handler/DataMetaObjectHandler.kt new file mode 100644 index 0000000..e667eec --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/handler/DataMetaObjectHandler.kt @@ -0,0 +1,26 @@ +package top.fatweb.oxygen.api.handler + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler +import org.apache.ibatis.reflection.MetaObject +import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.time.ZoneOffset + +/** + * Date meta object handler + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MetaObjectHandler + */ +@Component +class DataMetaObjectHandler : MetaObjectHandler { + override fun insertFill(metaObject: MetaObject?) { + this.strictInsertFill(metaObject, "createTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC)) + this.strictInsertFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC)) + } + + override fun updateFill(metaObject: MetaObject?) { + this.strictUpdateFill(metaObject, "updateTime", LocalDateTime::class.java, LocalDateTime.now(ZoneOffset.UTC)) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/handler/ExceptionHandler.kt b/src/main/kotlin/top/fatweb/oxygen/api/handler/ExceptionHandler.kt new file mode 100644 index 0000000..587f5a1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/handler/ExceptionHandler.kt @@ -0,0 +1,287 @@ +package top.fatweb.oxygen.api.handler + +import com.auth0.jwt.exceptions.JWTDecodeException +import com.auth0.jwt.exceptions.SignatureVerificationException +import com.auth0.jwt.exceptions.TokenExpiredException +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.dao.DuplicateKeyException +import org.springframework.http.converter.HttpMessageNotReadableException +import org.springframework.jdbc.BadSqlGrammarException +import org.springframework.jdbc.UncategorizedSQLException +import org.springframework.security.access.AccessDeniedException +import org.springframework.security.authentication.* +import org.springframework.web.HttpRequestMethodNotSupportedException +import org.springframework.web.bind.MethodArgumentNotValidException +import org.springframework.web.bind.annotation.ExceptionHandler +import org.springframework.web.bind.annotation.RestControllerAdvice +import org.springframework.web.servlet.resource.NoResourceFoundException +import top.fatweb.avatargenerator.AvatarException +import top.fatweb.oxygen.api.entity.common.ResponseCode +import top.fatweb.oxygen.api.entity.common.ResponseResult +import top.fatweb.oxygen.api.exception.* + +/** + * Exception handler + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@RestControllerAdvice +class ExceptionHandler { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + /** + * Handle all exception + * + * @param e Exception + * @return Response object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Exception + * @see ResponseResult + */ + @ExceptionHandler(value = [Exception::class]) + fun exceptionHandler(e: Exception): ResponseResult<*> { + return when (e) { + /* Request */ + is HttpRequestMethodNotSupportedException, is NoResourceFoundException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_ILLEGAL, e.localizedMessage, null) + } + + is HttpMessageNotReadableException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_ILLEGAL, e.localizedMessage.split(":")[0], null) + } + + is MethodArgumentNotValidException -> { + logger.debug(e.localizedMessage, e) + val errorMessage = e.allErrors.map { error -> error.defaultMessage }.joinToString(". ") + ResponseResult.fail(ResponseCode.SYSTEM_ARGUMENT_NOT_VALID, errorMessage, null) + } + + is RequestTooFrequentException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_REQUEST_TOO_FREQUENT, e.localizedMessage, null) + } + + /* Authentication */ + is InsufficientAuthenticationException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_UNAUTHORIZED, e.localizedMessage, null) + } + + is LockedException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_USER_LOCKED, "User account has been locked", null) + } + + is AccountExpiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_USER_EXPIRED, "User account has expired", null) + } + + is CredentialsExpiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail( + ResponseCode.PERMISSION_USER_CREDENTIALS_EXPIRED, + "User credentials have expired", + null + ) + } + + is DisabledException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_USER_DISABLE, "User has been disabled", null) + } + + is TokenExpiredException, is TokenHasExpiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_TOKEN_HAS_EXPIRED, e.localizedMessage, null) + } + + is InternalAuthenticationServiceException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_USERNAME_NOT_FOUND, "Username not found", null) + } + + is BadCredentialsException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail( + ResponseCode.PERMISSION_LOGIN_USERNAME_PASSWORD_ERROR, + "Wrong user name or password", + null + ) + } + + is SignatureVerificationException, is JWTDecodeException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_TOKEN_ILLEGAL, "Token illegal", null) + } + + is AccessDeniedException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_ACCESS_DENIED, "Access Denied", null) + } + + is UserNotFoundException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_USER_NOT_FOUND, e.localizedMessage, null) + } + + is NoVerificationRequiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_NO_VERIFICATION_REQUIRED, e.localizedMessage, null) + } + + is VerificationCodeErrorOrExpiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_VERIFY_CODE_ERROR_OR_EXPIRED, e.localizedMessage, null) + } + + is AccountNeedInitException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_ACCOUNT_NEED_INIT, e.localizedMessage, null) + } + + is RetrieveCodeErrorOrExpiredException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_RETRIEVE_CODE_ERROR_OR_EXPIRED, e.localizedMessage, null) + } + + is AccountNeedResetPasswordException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_ACCOUNT_NEED_RESET_PASSWORD, e.localizedMessage, null) + } + + is InvalidCaptchaCodeException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_INVALID_CAPTCHA_CODE, e.localizedMessage, null) + } + + is NeedTwoFactorException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_NEED_TWO_FACTOR, e.localizedMessage, null) + } + + is AlreadyHasTwoFactorException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_ALREADY_HAS_TWO_FACTOR, e.localizedMessage, null) + } + + is NoTwoFactorFoundException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.PERMISSION_NO_TWO_FACTOR_FOUND, e.localizedMessage, null) + } + + is TwoFactorVerificationCodeErrorException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail( + ResponseCode.PERMISSION_TWO_FACTOR_VERIFICATION_CODE_ERROR, + e.localizedMessage, + null + ) + } + + /* SQL */ + is DatabaseSelectException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.databaseFail(ResponseCode.DATABASE_SELECT_FAILED, e.localizedMessage, null) + } + + is DatabaseInsertException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.databaseFail(ResponseCode.DATABASE_INSERT_FAILED, e.localizedMessage, null) + } + + is DatabaseUpdateException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.databaseFail(ResponseCode.DATABASE_UPDATE_FAILED, e.localizedMessage, null) + } + + is DatabaseDeleteException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.databaseFail(ResponseCode.DATABASE_DELETE_FAILED, e.localizedMessage, null) + } + + is BadSqlGrammarException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.DATABASE_EXECUTE_ERROR, "Incorrect SQL syntax", null) + } + + is DuplicateKeyException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.DATABASE_DUPLICATE_KEY, "Duplicate key", null) + } + + is NoRecordFoundException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.DATABASE_NO_RECORD_FOUND, e.localizedMessage, null) + } + + is UncategorizedSQLException -> { + if (e.localizedMessage.contains("SQLITE_CONSTRAINT_UNIQUE")) { + logger.debug(e.localizedMessage, e) + return ResponseResult.fail(ResponseCode.DATABASE_DUPLICATE_KEY, "Duplicate key", null) + } + + logger.error(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.DATABASE_EXECUTE_ERROR, e.localizedMessage, null) + } + + is RecordAlreadyExists -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.DATABASE_RECORD_ALREADY_EXISTS, e.localizedMessage, null) + } + + /* Tool */ + is IllegalVersionException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_ILLEGAL_VERSION, e.localizedMessage, null) + } + + is ToolUnderReviewException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_UNDER_REVIEW, e.localizedMessage, null) + } + + is ToolNotUnderReviewException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_NOT_UNDER_REVIEW, e.localizedMessage, null) + } + + is ToolHasUnpublishedVersionException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_HAS_UNPUBLISHED_VERSION, e.localizedMessage, null) + } + + is ToolHasNotBeenPublishedException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_HAS_NOT_BEEN_PUBLISHED, e.localizedMessage, null) + } + + is ToolHasBeenPublishedException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.TOOL_HAS_BEEN_PUBLISHED, e.localizedMessage, null) + } + + /* Other */ + is MatchSensitiveWordException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_MATCH_SENSITIVE_WORD, e.localizedMessage, null) + } + + /* API */ + is AvatarException -> { + logger.debug(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.API_AVATAR_ERROR, e.localizedMessage, null) + } + + else -> { + logger.error(e.localizedMessage, e) + ResponseResult.fail(ResponseCode.SYSTEM_ERROR, e.toString(), null) + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAccessDeniedHandler.kt b/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAccessDeniedHandler.kt new file mode 100644 index 0000000..294b888 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAccessDeniedHandler.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.handler + +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.access.AccessDeniedException +import org.springframework.security.web.access.AccessDeniedHandler +import org.springframework.stereotype.Component + +/** + * Jwt access denied handler + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AccessDeniedHandler + */ +@Component +class JwtAccessDeniedHandler : AccessDeniedHandler { + override fun handle( + request: HttpServletRequest?, response: HttpServletResponse?, accessDeniedException: AccessDeniedException? + ) { + throw accessDeniedException ?: AccessDeniedException("Access Denied") + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAuthenticationEntryPointHandler.kt b/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAuthenticationEntryPointHandler.kt new file mode 100644 index 0000000..b215232 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/handler/JwtAuthenticationEntryPointHandler.kt @@ -0,0 +1,24 @@ +package top.fatweb.oxygen.api.handler + +import jakarta.servlet.http.HttpServletRequest +import jakarta.servlet.http.HttpServletResponse +import org.springframework.security.core.AuthenticationException +import org.springframework.security.web.AuthenticationEntryPoint +import org.springframework.stereotype.Component + +/** + * Jwt authentication entry point handler + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AuthenticationEntryPoint + */ +@Component +class JwtAuthenticationEntryPointHandler : AuthenticationEntryPoint { + override fun commence( + request: HttpServletRequest?, response: HttpServletResponse?, authException: AuthenticationException? + ) { + request?.setAttribute("filter.error", authException) + request?.getRequestDispatcher("/error/thrown")?.forward(request, response) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/http/TurnstileApi.kt b/src/main/kotlin/top/fatweb/oxygen/api/http/TurnstileApi.kt new file mode 100644 index 0000000..7f28258 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/http/TurnstileApi.kt @@ -0,0 +1,33 @@ +package top.fatweb.oxygen.api.http + +import com.github.lianjiatech.retrofit.spring.boot.core.RetrofitClient +import org.springframework.stereotype.Service +import retrofit2.http.Field +import retrofit2.http.FormUrlEncoded +import retrofit2.http.POST +import top.fatweb.oxygen.api.http.entity.turnstile.SiteverifyResponse +import top.fatweb.oxygen.api.properties.ServerProperties + +/** + * Turnstile http request api + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Service +@RetrofitClient(baseUrl = "https://challenges.cloudflare.com/turnstile/v0/") +interface TurnstileApi { + /** + * Turnstile post verify captcha code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SiteverifyResponse + */ + @FormUrlEncoded + @POST("siteverify") + fun siteverify( + @Field("response") captchaCode: String, + @Field("secret") secret: String = ServerProperties.turnstileSecretKey + ): SiteverifyResponse +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/http/entity/turnstile/SiteverifyResponse.kt b/src/main/kotlin/top/fatweb/oxygen/api/http/entity/turnstile/SiteverifyResponse.kt new file mode 100644 index 0000000..e6e6b94 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/http/entity/turnstile/SiteverifyResponse.kt @@ -0,0 +1,48 @@ +package top.fatweb.oxygen.api.http.entity.turnstile + +import com.fasterxml.jackson.annotation.JsonProperty +import java.time.LocalDateTime + +/** + * Turnstile verify captcha code response + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class SiteverifyResponse( + /** + * Is success + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonProperty("success") + val success: Boolean, + + /** + * Challenge time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonProperty("challenge_ts") + val challengeTs: LocalDateTime?, + + /** + * Hostname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonProperty("hostname") + val hostname: String?, + + /** + * Error codes list + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonProperty("error-codes") + val errorCodes: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/FuncMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/FuncMapper.kt new file mode 100644 index 0000000..167353d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/FuncMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.Func + +/** + * Function mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Func + */ +@Mapper +interface FuncMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/GroupMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/GroupMapper.kt new file mode 100644 index 0000000..8413f65 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/GroupMapper.kt @@ -0,0 +1,57 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.permission.Group + +/** + * Group mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Group + */ +@Mapper +interface GroupMapper : BaseMapper { + /** + * Select group in page + * + * @param page Pagination + * @param searchName Name to search for + * @param searchRegex Use regex + * @return Group in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectPage( + page: IPage, + @Param("searchName") searchName: String?, + @Param("searchRegex") searchRegex: Boolean + ): IPage + + /** + * Select group with role list by list of group IDs + * + * @param groupIds List of group IDs + * @return Group with role list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Group + */ + fun selectListWithRoleByIds(@Param("groupIds") groupIds: List): List? + + /** + * Select one group by ID + * + * @param id Group ID + * @return Group object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Group + */ + fun selectOneById(@Param("id") id: Long): Group? +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/MenuMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/MenuMapper.kt new file mode 100644 index 0000000..1952dcd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/MenuMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.Menu + +/** + * Menu mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Menu + */ +@Mapper +interface MenuMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/ModuleMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/ModuleMapper.kt new file mode 100644 index 0000000..bc2e492 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/ModuleMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.Module + +/** + * Module mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Module + */ +@Mapper +interface ModuleMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/OperationMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/OperationMapper.kt new file mode 100644 index 0000000..848a5bf --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/OperationMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.Operation + +/** + * Operation mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Operation + */ +@Mapper +interface OperationMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/PowerMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/PowerMapper.kt new file mode 100644 index 0000000..1100fdd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/PowerMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.Power + +/** + * Power mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Power + */ +@Mapper +interface PowerMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RPowerRoleMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RPowerRoleMapper.kt new file mode 100644 index 0000000..be569f3 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RPowerRoleMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.RPowerRole + +/** + * Power role intermediate mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see RPowerRole + */ +@Mapper +interface RPowerRoleMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RRoleGroupMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RRoleGroupMapper.kt new file mode 100644 index 0000000..48f10da --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RRoleGroupMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.RRoleGroup + +/** + * Role group intermediate mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see RRoleGroup + */ +@Mapper +interface RRoleGroupMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserGroupMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserGroupMapper.kt new file mode 100644 index 0000000..fa0b8bb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserGroupMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.RUserGroup + +/** + * User group intermediate mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see RUserGroup + */ +@Mapper +interface RUserGroupMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserRoleMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserRoleMapper.kt new file mode 100644 index 0000000..b6a18f4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RUserRoleMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.RUserRole + +/** + * User role intermediate mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see RUserRole + */ +@Mapper +interface RUserRoleMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RoleMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RoleMapper.kt new file mode 100644 index 0000000..b036bee --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/RoleMapper.kt @@ -0,0 +1,57 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.permission.Role + +/** + * Role mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Role + */ +@Mapper +interface RoleMapper : BaseMapper { + /** + * Select role in page + * + * @param page Pagination + * @param searchName Name to search for + * @param searchRegex Use regex + * @return Role in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectPage( + page: IPage, + @Param("searchName") searchName: String?, + @Param("searchRegex") searchRegex: Boolean + ): IPage + + /** + * Select role with power list by list of role IDs + * + * @param roleIds List of role IDs + * @return Role with power list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + */ + fun selectListWithPowerByIds(@Param("roleIds") roleIds: List): List? + + /** + * Select one role by ID + * + * @param id Role ID + * @return Role object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Role + */ + fun selectOneById(@Param("id") id: Long): Role? +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserInfoMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserInfoMapper.kt new file mode 100644 index 0000000..8e816d6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserInfoMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.permission.UserInfo + +/** + * User information mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see UserInfo + */ +@Mapper +interface UserInfoMapper : BaseMapper diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserMapper.kt new file mode 100644 index 0000000..80c1aad --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/permission/UserMapper.kt @@ -0,0 +1,111 @@ +package top.fatweb.oxygen.api.mapper.permission + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.permission.User + +/** + * User mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see User + */ +@Mapper +interface UserMapper : BaseMapper { + /** + * Select one user with power and information by username or email + * + * @param account Username or email + * @return User object with power and information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun selectOneWithPowerInfoByAccount(@Param("account") account: String): User? + + /** + * Select one user with basic information by username + * + * @param username Username + * @return User object with basic information + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun selectOneWithBasicInfoByUsername(@Param("username") username: String): User? + + /** + * Select user ID in page + * + * @param page Pagination + * @param searchType Type of search + * @param searchValue Value to search for + * @param searchRegex Use regex + * @return User in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectPage( + page: IPage, + @Param("searchType") searchType: String, + @Param("searchValue") searchValue: String?, + @Param("searchRegex") searchRegex: Boolean + ): IPage + + /** + * Select user with role and information list by list of user IDs + * + * @param userIds List of user IDs + * @return User with role and information list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun selectListWithRoleInfoByIds(@Param("userIds") userIds: List): List + + /** + * Select one user by ID + * + * @param id User ID + * @return User object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun selectOneWithRoleInfoById(@Param("id") id: Long): User? + + /** + * Select all user with information list + * + * @return User with information list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun selectListWithInfo(): List + + /** + * Select user IDs list by list of role IDs + * + * @param roleIds List of role IDs + * @return User IDs list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun selectIdsWithRoleIds(@Param("roleIds") roleIds: List): List + + /** + * Select user IDs list by list of group IDs + * + * @param groupIds List of group IDs + * @return User IDs list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun selectIdsWithGroupIds(@Param("groupIds") groupIds: List): List +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/EventLogMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/EventLogMapper.kt new file mode 100644 index 0000000..aaf96e0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/EventLogMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.system + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.system.EventLog + +/** + * Event log mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see EventLog + */ +@Mapper +interface EventLogMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SensitiveWordMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SensitiveWordMapper.kt new file mode 100644 index 0000000..be01e0c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SensitiveWordMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.system + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.system.SensitiveWord + +/** + * Sensitive word mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see SensitiveWord + */ +@Mapper +interface SensitiveWordMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/StatisticsLogMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/StatisticsLogMapper.kt new file mode 100644 index 0000000..59b1523 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/StatisticsLogMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.system + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.system.StatisticsLog + +/** + * Statistics log mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see StatisticsLog + */ +@Mapper +interface StatisticsLogMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SysLogMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SysLogMapper.kt new file mode 100644 index 0000000..06088a2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/system/SysLogMapper.kt @@ -0,0 +1,44 @@ +package top.fatweb.oxygen.api.mapper.system + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.system.SysLog +import java.time.LocalDateTime + +/** + * System log mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see SysLog + */ +@Mapper +interface SysLogMapper : BaseMapper { + /** + * Select system log in page + * + * @param page Pagination + * @param logType List of log types + * @param requestMethod List of request methods + * @param searchRequestUrl Request URL to search for + * @param searchStartTime Start time to search for + * @param searchEndTime end time to search for + * @return System log in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + * @see SysLog + * @see LocalDateTime + */ + fun selectPage( + page: IPage, + @Param("logType") logType: List?, + @Param("requestMethod") requestMethod: List?, + @Param("searchRequestUrl") searchRequestUrl: String?, + @Param("searchStartTime") searchStartTime: LocalDateTime?, + @Param("searchEndTime") searchEndTime: LocalDateTime? + ): IPage +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/EditMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/EditMapper.kt new file mode 100644 index 0000000..5f6bbcc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/EditMapper.kt @@ -0,0 +1,88 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.entity.tool.ToolTemplate + +/** + * Tool edit mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Tool + */ +@Mapper +interface EditMapper : BaseMapper { + /** + * Select tool template by ID + * + * @param id Template ID + * @return ToolTemplate object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplate + */ + fun selectTemplate(@Param("id") id: Long): ToolTemplate? + + /** + * Select tool by ID and user ID + * + * @param id Tool ID + * @param userId User ID + * @return Tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + */ + fun selectOne(@Param("id") id: Long, @Param("userId") userId: Long): Tool? + + /** + * Select tool ID by user ID in page + * + * @param page Pagination + * @param userId User ID + * @return Tool ID in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectPersonalToolIdPage(page: IPage, @Param("userId") userId: Long): IPage + + /** + * Select tool in list by tool IDs and user ID + * + * @param toolIds List of tool Ids + * @param userId User ID + * @return List of Tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + */ + fun selectListByToolIds(@Param("toolIds") toolIds: List, @Param("userId") userId: Long): List + + /** + * Select tool detail + * + * @param username Username + * @param toolId Tool ID + * @param ver Tool version + * @param operator Operator username + * @return List of tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + * @see Tool + */ + fun selectDetail( + @Param("username") username: String, + @Param("toolId") toolId: String, + @Param("ver") ver: String, + @Param("platform") platform: Platform, + @Param("operator") operator: String? + ): Tool? +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ManagementMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ManagementMapper.kt new file mode 100644 index 0000000..0ef1619 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ManagementMapper.kt @@ -0,0 +1,62 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.tool.Tool + +/** + * Tool management mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Tool + */ +@Mapper +interface ManagementMapper : BaseMapper { + /** + * Select tool by ID + * + * @param id Tool ID + * @return Tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + */ + fun selectOne(@Param("id") id: Long): Tool? + + /** + * Select tool ID in page + * + * @param page Pagination + * @param review Review + * @param searchType Type of search + * @param searchValue Value to search for + * @param searchRegex Use regex + * @return Tool ID in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectPage( + page: IPage, + @Param("review") review: List?, + @Param("platform") platform: List?, + @Param("searchType") searchType: String, + @Param("searchValue") searchValue: String?, + @Param("searchRegex") searchRegex: Boolean + ): IPage + + /** + * Select tool in list by tool IDs + * + * @param ids List of tool IDs + * @return List of tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + */ + fun selectListByIds(@Param("ids") ids: List): List +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/RToolCategoryMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/RToolCategoryMapper.kt new file mode 100644 index 0000000..1bc8be4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/RToolCategoryMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.tool.RToolCategory + +/** + * Tool category intermediate mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see RToolCategory + */ +@Mapper +interface RToolCategoryMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/StoreMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/StoreMapper.kt new file mode 100644 index 0000000..4d3a11d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/StoreMapper.kt @@ -0,0 +1,73 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.entity.tool.Tool + +/** + * Tool store mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see Tool + */ +@Mapper +interface StoreMapper : BaseMapper { + /** + * Select author and tool ID in page + * + * @param page Pagination + * @param searchValue Value to search for + * @return Author:Tool_ID in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectAuthorToolIdPage( + page: IPage, + @Param("searchValue") searchValue: String?, + @Param("platform") platform: Platform? = null + ): IPage + + /** + * Select author and tool ID by username in page + * + * @param page Pagination + * @param username Username + * @return Author:Tool_ID in page + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IPage + */ + fun selectAuthorToolIdPageByUsername(page: IPage, @Param("username") username: String): IPage + + /** + * Select tool in list by Author:Tool_ID + * + * @param ids List of Author:Tool_ID + * @return List of tool object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool + */ + fun selectListByAuthorToolIds( + @Param("ids") ids: List, + @Param("operator") operator: Long?, + @Param("platform") platform: Platform? = null + ): List + + /** + * Count published tool by username and toolId + * + * @param authorId Author ID + * @param toolId Tool ID + * @return Number + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun countPublishedToolByAuthorAndToolId(@Param("authorId") authorId: Long, @Param("toolId") toolId: String): Long +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolBaseMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolBaseMapper.kt new file mode 100644 index 0000000..26f470f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolBaseMapper.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.tool.ToolBase + +/** + * Tool base mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see ToolBase + */ +@Mapper +interface ToolBaseMapper : BaseMapper { + /** + * Select tool base by ID + * + * @param id Tool base ID + * @return ToolBase object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBase + */ + fun selectOne(@Param("id") id: Long): ToolBase? +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolCategoryMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolCategoryMapper.kt new file mode 100644 index 0000000..414eb72 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolCategoryMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.tool.ToolCategory + +/** + * Tool category mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see ToolCategory + */ +@Mapper +interface ToolCategoryMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolDataMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolDataMapper.kt new file mode 100644 index 0000000..088f219 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolDataMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.tool.ToolData + +/** + * Tool data mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see ToolData + */ +@Mapper +interface ToolDataMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolFavoriteMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolFavoriteMapper.kt new file mode 100644 index 0000000..213975b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolFavoriteMapper.kt @@ -0,0 +1,16 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import org.apache.ibatis.annotations.Mapper +import top.fatweb.oxygen.api.entity.tool.ToolFavorite + +/** + * Tool favorite mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see ToolFavorite + */ +@Mapper +interface ToolFavoriteMapper : BaseMapper \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolTemplateMapper.kt b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolTemplateMapper.kt new file mode 100644 index 0000000..d009b4c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/mapper/tool/ToolTemplateMapper.kt @@ -0,0 +1,42 @@ +package top.fatweb.oxygen.api.mapper.tool + +import com.baomidou.mybatisplus.core.mapper.BaseMapper +import com.baomidou.mybatisplus.core.metadata.IPage +import org.apache.ibatis.annotations.Mapper +import org.apache.ibatis.annotations.Param +import top.fatweb.oxygen.api.entity.tool.ToolTemplate + +/** + * Tool template mapper + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseMapper + * @see ToolTemplate + */ +@Mapper +interface ToolTemplateMapper : BaseMapper { + /** + * Select tool template by ID + * + * @param id Tool template ID + * @return ToolTemplate object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplate + */ + fun selectOne(@Param("id") id: Long): ToolTemplate? + + /** + * Select tool template in list + * + * @return List of ToolTemplate object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplate + */ + fun selectListWithBaseName( + page: IPage, + @Param("platform") platform: List? + ): IPage +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/CaptchaCodeParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/CaptchaCodeParam.kt new file mode 100644 index 0000000..876e665 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/CaptchaCodeParam.kt @@ -0,0 +1,25 @@ +package top.fatweb.oxygen.api.param + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Captcha code parameter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +open class CaptchaCodeParam { + /** + * Captcha code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "验证码", required = true) + @field:NotBlank(message = "Captcha code can not be blank") + var captchaCode: String? = null +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/PageSortParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/PageSortParam.kt new file mode 100644 index 0000000..cab7e95 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/PageSortParam.kt @@ -0,0 +1,50 @@ +package top.fatweb.oxygen.api.param + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Min + +/** + * Page sort parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +open class PageSortParam { + /** + * Current page number + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "分页页码", defaultValue = "1", example = "1") + @field:Min(1, message = "Pagination page number must be a positive integer") + var currentPage: Long = 1 + + /** + * Size of page + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "分页大小", defaultValue = "20", example = "20") + @field:Min(1, message = "The number of data per page must be a positive integer") + var pageSize: Long = 20 + + /** + * Field name to sort + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "排序字段", example = "id") + var sortField: String? = null + + /** + * Sort order by + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "排序方式", allowableValues = ["desc", "asc"], defaultValue = "desc", example = "desc") + var sortOrder: String? = null +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarBaseParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarBaseParam.kt new file mode 100644 index 0000000..c949017 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarBaseParam.kt @@ -0,0 +1,69 @@ +package top.fatweb.oxygen.api.param.api.v1.avatar + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Max +import jakarta.validation.constraints.Pattern + +/** + * Avatar base parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +open class AvatarBaseParam { + /** + * Seed to generate avatar + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "种子") + var seed: Long? = null + + /** + * Size of image + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "图像大小", defaultValue = "128") + @field:Max(256, message = "Size must be less than or equal to 256") + var size: Int? = null + + /** + * Margin of image + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "外边距", defaultValue = "0") + var margin: Int? = null + + /** + * Padding of image + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "内边距", defaultValue = "0") + var padding: Int? = null + + /** + * List of colors to generate avatar + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "颜色列表", example = "#FFFFFFAA") + var colors: List? = null + + /** + * Background of image + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "背景颜色", example = "#FFFFFFAA") + @field:Pattern(regexp = "^#[0-9a-fA-F]{6}|#[0-9a-fA-F]{8}$", message = "Background color must be a hex color code") + var background: String? = null +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarGitHubParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarGitHubParam.kt new file mode 100644 index 0000000..6ea06a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/api/v1/avatar/AvatarGitHubParam.kt @@ -0,0 +1,32 @@ +package top.fatweb.oxygen.api.param.api.v1.avatar + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Max + +/** + * GitHub style avatar parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + */ +data class AvatarGitHubParam( + /** + * Size of element + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "元素大小", defaultValue = "400") + @field:Max(1000, message = "Element size must be less than or equal to 1000") + val elementSize: Int = 400, + + /** + * Precision of element + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "精确度", defaultValue = "5") + val precision: Int = 5 +) : AvatarBaseParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/ForgetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/ForgetParam.kt new file mode 100644 index 0000000..2d02d13 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/ForgetParam.kt @@ -0,0 +1,30 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.CaptchaCodeParam + +/** + * Forget password parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see CaptchaCodeParam + */ +@Trim +@Schema(description = "忘记密码请求参数") +data class ForgetParam( + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "邮箱", required = true, example = "user@email.com") + @field:NotBlank(message = "Email can not be blank") + @field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address") + var email: String? +) : CaptchaCodeParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/LoginParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/LoginParam.kt new file mode 100644 index 0000000..c00f95c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/LoginParam.kt @@ -0,0 +1,47 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.CaptchaCodeParam + +/** + * Login parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see CaptchaCodeParam + */ +@Trim +@Schema(description = "登录请求参数") +data class LoginParam( + /** + * Account + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "账户", required = true, example = "test") + @field:NotBlank(message = "Account can not be blank") + var account: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码", required = true) + @field:NotBlank(message = "Password can not be blank") + val password: String?, + + /** + * Two-factor code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "双因素验证码") + val twoFactorCode: String? +) : CaptchaCodeParam() \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RegisterParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RegisterParam.kt new file mode 100644 index 0000000..59bf97d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RegisterParam.kt @@ -0,0 +1,54 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern +import jakarta.validation.constraints.Size +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.CaptchaCodeParam + +/** + * Register parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see CaptchaCodeParam + */ +@Trim +@Schema(description = "注册请求参数") +data class RegisterParam( + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户名", required = true, example = "abc") + @field:NotBlank(message = "Username can not be blank") + @field:Pattern(regexp = "[a-zA-Z-_][0-9a-zA-Z-_]{2,38}", message = "Illegal username") + var username: String?, + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "邮箱", required = true, example = "user@email.com") + @field:NotBlank(message = "Email can not be blank") + @field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address") + var email: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码", required = true) + @field:NotBlank(message = "Password can not be blank") + @field:Size(min = 10, max = 30, message = "Password must be 10-30 characters") + val password: String? +) : CaptchaCodeParam() \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RetrieveParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RetrieveParam.kt new file mode 100644 index 0000000..5163e5f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/RetrieveParam.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size +import top.fatweb.oxygen.api.param.CaptchaCodeParam + +/** + * Retrieve password parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see CaptchaCodeParam + */ +@Schema(description = "找回密码请求参数") +data class RetrieveParam( + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证码", required = true) + @field:NotBlank(message = "Code can not be blank") + val code: String?, + + /** + * New password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "新密码") + @field:NotBlank(message = "New password can not be blank") + @field:Size(min = 10, max = 30, message = "New password must be 10-30 characters") + val password: String? +) : CaptchaCodeParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorRemoveParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorRemoveParam.kt new file mode 100644 index 0000000..7a242fb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorRemoveParam.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +/** + * Remove two-factor parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "移除双因素请求参数") +data class TwoFactorRemoveParam( + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证码") + @field:NotBlank(message = "Code can not be blank") + val code: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorValidateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorValidateParam.kt new file mode 100644 index 0000000..1a6230a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/TwoFactorValidateParam.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +/** + * Validate two-factor parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "验证双因素请求参数") +data class TwoFactorValidateParam( + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证码") + @field:NotBlank(message = "Code can not be blank") + val code: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/VerifyParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/VerifyParam.kt new file mode 100644 index 0000000..ebe1cd3 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/VerifyParam.kt @@ -0,0 +1,46 @@ +package top.fatweb.oxygen.api.param.permission + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Verify email parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "验证邮箱请求参数") +data class VerifyParam( + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证码", required = true) + @field:NotBlank(message = "Code can not be blank") + val code: String?, + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "昵称", example = "QwQ") + @field:Size(min = 3, max = 20, message = "Nickname must be 3-20 characters") + var nickname: String?, + + /** + * Avatar + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "头像") + val avatar: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupAddParam.kt new file mode 100644 index 0000000..b5e4af8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupAddParam.kt @@ -0,0 +1,44 @@ +package top.fatweb.oxygen.api.param.permission.group + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Add group parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "用户组添加请求参数") +data class GroupAddParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户组名称", required = true, example = "Group_1") + @field:NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * List of role IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID 列表") + val roleIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupDeleteParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupDeleteParam.kt new file mode 100644 index 0000000..ff08c35 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupDeleteParam.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.param.permission.group + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotEmpty + +/** + * Delete group parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户组删除请求参数") +data class GroupDeleteParam( + /** + * List of group IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组 ID 列表", required = true) + @field: NotEmpty(message = "Ids can not be empty") + val ids: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupGetParam.kt new file mode 100644 index 0000000..73ecff0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupGetParam.kt @@ -0,0 +1,40 @@ +package top.fatweb.oxygen.api.param.permission.group + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get group parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +@Schema(description = "用户组查询请求参数") +data class GroupGetParam( + /** + * Name to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询用户组名称", example = "Group_1") + var searchName: String?, + + /** + * Use regex + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "查询使用正则表达式", + allowableValues = ["true", "false"], + defaultValue = "false", + example = "false" + ) + val searchRegex: Boolean = false, +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateParam.kt new file mode 100644 index 0000000..509de1f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateParam.kt @@ -0,0 +1,55 @@ +package top.fatweb.oxygen.api.param.permission.group + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update group parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "用户组更新请求参数") +data class GroupUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组 ID", required = true) + @field:NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户组名称", required = true, example = "Group_1") + @field:NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * List of role IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID 列表") + val roleIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateStatusParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateStatusParam.kt new file mode 100644 index 0000000..2676a93 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/group/GroupUpdateStatusParam.kt @@ -0,0 +1,32 @@ +package top.fatweb.oxygen.api.param.permission.group + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull + +/** + * Update status of group parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户组更改状态请求参数") +data class GroupUpdateStatusParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组 ID", required = true) + @field:NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleAddParam.kt new file mode 100644 index 0000000..ad7aaf6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleAddParam.kt @@ -0,0 +1,44 @@ +package top.fatweb.oxygen.api.param.permission.role + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Add role parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "角色添加请求参数") +data class RoleAddParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "角色名称", required = true, example = "Role_1") + @field:NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * List of power IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "权限 ID 列表") + val powerIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleDeleteParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleDeleteParam.kt new file mode 100644 index 0000000..0704b2a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleDeleteParam.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.param.permission.role + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotEmpty + +/** + * Delete role parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "角色删除请求参数") +data class RoleDeleteParam( + /** + * List of role IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID 列表", required = true) + @field: NotEmpty(message = "Ids can not be empty") + val ids: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleGetParam.kt new file mode 100644 index 0000000..f6d8280 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleGetParam.kt @@ -0,0 +1,40 @@ +package top.fatweb.oxygen.api.param.permission.role + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get role parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +@Schema(description = "角色查询请求参数") +data class RoleGetParam( + /** + * Name to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询角色名称", example = "Role_1") + var searchName: String?, + + /** + * Use regex + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "查询使用正则表达式", + allowableValues = ["true", "false"], + defaultValue = "false", + example = "false" + ) + val searchRegex: Boolean = false, +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateParam.kt new file mode 100644 index 0000000..40cbd57 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateParam.kt @@ -0,0 +1,55 @@ +package top.fatweb.oxygen.api.param.permission.role + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update role parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "角色更新请求参数") +data class RoleUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID", required = true) + @field:NotNull(message = "Role id can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "角色名称", required = true, example = "Role_1") + @field:NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * List of power IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "权限 ID 列表") + val powerIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateStatusParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateStatusParam.kt new file mode 100644 index 0000000..5fda3c1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/role/RoleUpdateStatusParam.kt @@ -0,0 +1,32 @@ +package top.fatweb.oxygen.api.param.permission.role + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull + +/** + * Update status of role parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "角色更改状态请求参数") +data class RoleUpdateStatusParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID", required = true) + @field:NotNull(message = "Role id can not be null") + val id: Long?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserAddParam.kt new file mode 100644 index 0000000..98e19b9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserAddParam.kt @@ -0,0 +1,134 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim +import java.time.LocalDateTime + +/** + * Add user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "添加用户请求参数") +data class UserAddParam( + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户名", required = true, example = "User_1") + @field:NotBlank(message = "Username can not be blank") + var username: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码(为空自动生成随机密码)") + val password: String?, + + /** + * Verified + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否已验证", allowableValues = ["true", "false"], defaultValue = "false", example = "false") + val verified: Boolean = false, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "锁定", allowableValues = ["true", "false"], defaultValue = "false", example = "false") + val locking: Boolean = false, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "昵称", required = true, example = "Nickname_1") + @field:NotBlank(message = "Nickname can not be blank") + var nickname: String?, + + /** + * Avatar base63 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "头像") + val avatar: String?, + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "邮箱", required = true, example = "user@email.com") + @field:NotBlank(message = "Email can not be blank") + @field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address") + var email: String?, + + /** + * List of role IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID 列表") + val roleIds: List?, + + /** + * List of group IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组 ID 列表") + val groupIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserChangePasswordParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserChangePasswordParam.kt new file mode 100644 index 0000000..094c3b5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserChangePasswordParam.kt @@ -0,0 +1,35 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size + +/** + * Change password of user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户更改密码请求参数") +data class UserChangePasswordParam( + /** + * Original password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "原密码", required = true) + @field:NotBlank(message = "Original password can not be blank") + val originalPassword: String?, + + /** + * New password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "原密码", required = true) + @field:NotBlank(message = "New password can not be blank") + @field:Size(min = 10, max = 30, message = "New password must be 10-30 characters") + val newPassword: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserDeleteParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserDeleteParam.kt new file mode 100644 index 0000000..714a096 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserDeleteParam.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotEmpty + +/** + * Delete user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "删除用户请求参数") +data class UserDeleteParam( + /** + * List of user IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户 ID 列表", required = true) + @field: NotEmpty(message = "Ids can not be empty") + val ids: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserGetParam.kt new file mode 100644 index 0000000..358fe52 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserGetParam.kt @@ -0,0 +1,54 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +@Schema(description = "查询用户请求参数") +data class UserGetParam( + /** + * Type of search + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "搜索类型", + allowableValues = ["ALL", "ID", "USERNAME", "NICKNAME", "EMAIL"], + defaultValue = "ALL", + example = "ALL" + ) + val searchType: String = "ALL", + + /** + * Value to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询内容", example = "User_1") + var searchValue: String?, + + /** + * Use regex + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "查询使用正则表达式", + allowableValues = ["true", "false"], + defaultValue = "false", + example = "false" + ) + val searchRegex: Boolean = false, +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserInfoUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserInfoUpdateParam.kt new file mode 100644 index 0000000..7928cf6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserInfoUpdateParam.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Size +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update user information parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "更新用户信息请求参数") +data class UserInfoUpdateParam( + /** + * Avatar base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "头像") + val avatar: String?, + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "昵称", example = "QwQ") + @field:NotBlank(message = "Nickname can not be blank") + @field:Size(min = 3, max = 20, message = "Nickname must be 3-20 characters") + var nickname: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdateParam.kt new file mode 100644 index 0000000..eab624a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdateParam.kt @@ -0,0 +1,136 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim +import java.time.LocalDateTime + +/** + * Update user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "更新用户请求参数") +data class UserUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户 ID", required = true) + @field:NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户名", required = true, example = "User_1") + @field:NotBlank(message = "Username can not be blank") + var username: String?, + + /** + * Verified + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否已验证", allowableValues = ["true", "false"], defaultValue = "false", example = "false") + val verified: Boolean = false, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "锁定", allowableValues = ["true", "false"], defaultValue = "false", example = "false") + val locking: Boolean = false, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true", example = "true") + val enable: Boolean = true, + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "昵称", required = true, example = "Nickname_1") + @field:NotBlank(message = "Nickname can not be blank") + var nickname: String?, + + /** + * Avatar base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "头像") + val avatar: String?, + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "邮箱", required = true, example = "user@email.com") + @field:NotBlank(message = "Email can not be blank") + @field:Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*\$", message = "Illegal email address") + var email: String?, + + /** + * List of role IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色 ID 列表") + val roleIds: List?, + + /** + * List of group IDs + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组 ID 列表") + val groupIds: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdatePasswordParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdatePasswordParam.kt new file mode 100644 index 0000000..3801ecd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/permission/user/UserUpdatePasswordParam.kt @@ -0,0 +1,45 @@ +package top.fatweb.oxygen.api.param.permission.user + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import java.time.LocalDateTime + +/** + * Update password of user parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "更改用户密码请求参数") +data class UserUpdatePasswordParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户 ID", required = true) + @field:NotNull(message = "ID can not be null") + val id: Long?, + + /** + * New password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "新密码", required = true) + @field:NotBlank(message = "Password can not be blank") + val password: String?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/ActiveInfoGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/ActiveInfoGetParam.kt new file mode 100644 index 0000000..de4c1ba --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/ActiveInfoGetParam.kt @@ -0,0 +1,46 @@ +package top.fatweb.oxygen.api.param.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.fasterxml.jackson.annotation.JsonValue +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Get active information parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户活跃信息请求参数") +data class ActiveInfoGetParam( + /** + * Scope + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "范围", + allowableValues = ["WEEK", "MONTH", "QUARTER", "YEAR", "TWO_YEARS", "THREE_YEARS", "FIVE_YEARS", "ALL"], + defaultValue = "WEEK", + example = "WEEK" + ) + val scope: Scope = Scope.WEEK +) { + enum class Scope(@field:EnumValue @field:JsonValue val code: String) { + WEEK("WEEK"), + + MONTH("MONTH"), + + QUARTER("QUARTER"), + + YEAR("YEAR"), + + TWO_YEARS("TWO_YEARS"), + + THREE_YEARS("THREE_YEARS"), + + FIVE_YEARS("FIVE_YEARS"), + + ALL("ALL") + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/BaseSettingsParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/BaseSettingsParam.kt new file mode 100644 index 0000000..16effd0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/BaseSettingsParam.kt @@ -0,0 +1,54 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Base settings parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "基础设置请求参数") +data class BaseSettingsParam( + /** + * Application name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "应用名称") + var appName: String?, + + /** + * Application URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "应用 URL") + var appUrl: String?, + + /** + * Verify URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "验证邮箱 URL") + var verifyUrl: String?, + + /** + * Retrieve URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "找回密码 URL") + var retrieveUrl: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSendParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSendParam.kt new file mode 100644 index 0000000..bdbdde1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSendParam.kt @@ -0,0 +1,26 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Mail send parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "邮件发送请求参数") +data class MailSendParam( + /** + * Receiver + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "接收者", required = true, example = "user@email.com") + @field:NotBlank(message = "Receiver cannot be blank") + var to: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSettingsParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSettingsParam.kt new file mode 100644 index 0000000..61284fd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/MailSettingsParam.kt @@ -0,0 +1,83 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.settings.MailSecurityType + +/** + * Mail settings parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "邮件设置请求参数") +data class MailSettingsParam( + /** + * Host + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "SMTP 服务器") + var host: String?, + + /** + * Port + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "端口号") + var port: Int?, + + /** + * Security type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "安全类型", allowableValues = ["None", "SSL/TLS", "StartTls"], defaultValue = "None") + val securityType: MailSecurityType = MailSecurityType.NONE, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "用户名") + var username: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码") + val password: String?, + + /** + * Sender + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "发送者") + var from: String?, + + /** + * Sender name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "发送者名称") + var fromName: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/OnlineInfoGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/OnlineInfoGetParam.kt new file mode 100644 index 0000000..d00d9eb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/OnlineInfoGetParam.kt @@ -0,0 +1,48 @@ +package top.fatweb.oxygen.api.param.system + +import com.baomidou.mybatisplus.annotation.EnumValue +import com.fasterxml.jackson.annotation.JsonValue +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Get online information parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "在线信息请求参数") +data class OnlineInfoGetParam( + /** + * Scope + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "范围", + allowableValues = ["WEEK", "MONTH", "QUARTER", "YEAR", "TWO_YEARS", "THREE_YEARS", "FIVE_YEARS", "ALL"], + defaultValue = "WEEK", + example = "WEEK" + ) + val scope: Scope = Scope.WEEK +) { + enum class Scope(@field:EnumValue @field:JsonValue val code: String) { + DAY("DAY"), + + WEEK("WEEK"), + + MONTH("MONTH"), + + QUARTER("QUARTER"), + + YEAR("YEAR"), + + TWO_YEARS("TWO_YEARS"), + + THREE_YEARS("THREE_YEARS"), + + FIVE_YEARS("FIVE_YEARS"), + + ALL("ALL") + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordAddParam.kt new file mode 100644 index 0000000..2324293 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordAddParam.kt @@ -0,0 +1,46 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.system.SensitiveWord + +/** + * Add sensitive word settings parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(defaultValue = "敏感词添加请求参数") +data class SensitiveWordAddParam( + /** + * Word + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "词", required = true) + @field:NotBlank(message = "Word can not be blank") + var word: String?, + + /** + * Use for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWord.Use + */ + @Schema(description = "用于", allowableValues = ["USERNAME"]) + val useFor: Set = emptySet(), + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true") + val enable: Boolean = true +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordUpdateParam.kt new file mode 100644 index 0000000..1faf759 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SensitiveWordUpdateParam.kt @@ -0,0 +1,15 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Update sensitive word settings parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(defaultValue = "敏感词修改请求参数") +data class SensitiveWordUpdateParam( + @Schema(description = "ID 列表") + val ids: Set = emptySet() +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/SysLogGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SysLogGetParam.kt new file mode 100644 index 0000000..8b3bfe0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/SysLogGetParam.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import org.springframework.format.annotation.DateTimeFormat +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.PageSortParam +import java.time.LocalDateTime + +/** + * Get system log parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +@Schema(description = "获取系统日志请求参数") +data class SysLogGetParam( + /** + * Log type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "类型过滤(多个使用逗号分隔)", + allowableValues = ["INFO", "LOGIN", "LOGOUT", "REGISTER", "STATISTICS", "API", "ERROR"], + example = "INFO" + ) + val logType: String?, + + /** + * Request method to filter + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "请求方式过滤(多个使用逗号分隔)", + allowableValues = ["GET", "POST", "PUT", "PATCH", "DELETE", "DELETE", "OPTIONS"], + example = "GET,POST" + ) + val requestMethod: String?, + + /** + * Request URL to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询请求 Url") + var searchRequestUrl: String?, + + /** + * Use regex + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + /* + @Schema(description = "查询使用正则表达式", allowableValues = ["true", "false"], defaultValue = "false") + val searchRegex: Boolean = false, + */ + + /** + * Start time to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "查询开始时间", example = "1900-01-01T00:00:00.000Z") + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + val searchStartTime: LocalDateTime?, + + /** + * End time to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "查询结束时间", example = "1900-01-01T00:00:00.000Z") + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") + val searchEndTime: LocalDateTime? +) : PageSortParam() \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/system/TwoFactorSettingsParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/system/TwoFactorSettingsParam.kt new file mode 100644 index 0000000..cb157e2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/system/TwoFactorSettingsParam.kt @@ -0,0 +1,37 @@ +package top.fatweb.oxygen.api.param.system + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.Min +import jakarta.validation.constraints.NotNull +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Two-factor settings parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "双因素设置请求参数") +data class TwoFactorSettingsParam( + /** + * Issuer + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "发布者") + var issuer: String?, + + /** + * Length of secret key + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密钥长度") + @field:NotNull(message = "Length of secret key can not be null") + @field:Min(value = 3, message = "The length of the key must be greater than or equal to 3") + val secretKeyLength: Int? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseAddParam.kt new file mode 100644 index 0000000..a221097 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseAddParam.kt @@ -0,0 +1,38 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.tool.Platform + +/** + * Add tool base parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolBaseAddParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称", required = true) + @field: NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "平台") + @field:NotNull(message = "Platform can not be null") + val platform: Platform? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseGetParam.kt new file mode 100644 index 0000000..4f51c57 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseGetParam.kt @@ -0,0 +1,27 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get tool base parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Schema(description = "获取工具基板请求参数") +data class ToolBaseGetParam( + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "平台过滤(多个使用逗号分隔)", + allowableValues = ["WEB", "DESKTOP", "ANDROID"], + example = "WEB" + ) + val platform: String? +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseUpdateParam.kt new file mode 100644 index 0000000..b798fc2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolBaseUpdateParam.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update tool base parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolBaseUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "ID", required = true) + @field: NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称") + @field: Pattern(regexp = "^.*\\S.*$", message = "Name can not be blank") + var name: String?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "源码") + @field: Pattern(regexp = "^.*\\S.*$", message = "Source can not be blank") + val source: String?, + + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "产物") + @field: Pattern(regexp = "^.*\\S.*$", message = "Dist can not be blank") + val dist: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryAddParam.kt new file mode 100644 index 0000000..2f615d0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryAddParam.kt @@ -0,0 +1,34 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Add tool category parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolCategoryAddParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称", required = true) + @field: NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true") + val enable: Boolean = true +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryUpdateParam.kt new file mode 100644 index 0000000..72c63b2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCategoryUpdateParam.kt @@ -0,0 +1,45 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update tool category parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolCategoryUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "ID", required = true) + @field: NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称") + @field: Pattern(regexp = "^.*\\S.*$", message = "Name can not be blank") + var name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"]) + val enable: Boolean? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCreateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCreateParam.kt new file mode 100644 index 0000000..379b51e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolCreateParam.kt @@ -0,0 +1,104 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotEmpty +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Create tool parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "创建工具请求参数") +data class ToolCreateParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称", required = true) + @field: NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "工具唯一 ID", required = true, example = "tool_a") + @field: NotBlank(message = "ToolId can not be blank") + @field: Pattern( + regexp = "^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$", + message = "ToolId can only match '^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$'" + ) + var toolId: String?, + + /** + * Icon + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "图标", required = true) + @field: NotBlank(message = "Icon can not be blank") + val icon: String?, + + /** + * Description + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "简介") + val description: String?, + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "版本", required = true, example = "1.0.3") + @field: NotBlank(message = "Ver can not be blank") + @field: Pattern(regexp = "^\\d+\\.\\d+\\.\\d+\$", message = "Ver can only match '..'") + val ver: String?, + + /** + * Template ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "模板 ID", required = true) + @field: NotNull(message = "TemplateId can not be null") + val templateId: Long?, + + /** + * Keywords + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "关键词", required = true) + @field: NotEmpty(message = "Keywords can not be empty") + val keywords: List?, + + /** + * Categories + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "类别", required = true) + @field: NotEmpty(message = "Categories can not be empty") + val categories: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteAddParam.kt new file mode 100644 index 0000000..28b6fc0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteAddParam.kt @@ -0,0 +1,40 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Add favorite tool parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class ToolFavoriteAddParam( + /** + * Author ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "作者 ID", required = true) + @field: NotBlank(message = "AuthorId cannot be blank") + var authorId: Long?, + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "工具唯一 ID", required = true) + @field: NotBlank(message = "ToolId cannot be blank") + @field: Pattern( + regexp = "^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$", + message = "ToolId can only match '^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$'" + ) + var toolId: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteRemoveParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteRemoveParam.kt new file mode 100644 index 0000000..17741ee --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolFavoriteRemoveParam.kt @@ -0,0 +1,40 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Remove favorite tool parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class ToolFavoriteRemoveParam( + /** + * Author ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "作者 ID", required = true) + @field: NotBlank(message = "AuthorId cannot be blank") + var authorId: Long?, + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "工具唯一 ID", required = true) + @field: NotBlank(message = "ToolId cannot be blank") + @field: Pattern( + regexp = "^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$", + message = "ToolId can only match '^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$'" + ) + var toolId: String?, +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementGetParam.kt new file mode 100644 index 0000000..4d98e5b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementGetParam.kt @@ -0,0 +1,79 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get tool parameters in tool management + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +data class ToolManagementGetParam( + /** + * Type of search + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "搜索类型", + allowableValues = ["ALL", "NAME", "TOOL_ID", "NICKNAME", "USERNAME", "KEYWORD"], + defaultValue = "ALL", + example = "ALL" + ) + val searchType: String = "ALL", + + /** + * Value to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询内容", example = "ToolName") + var searchValue: String?, + + /** + * Use regex + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "查询使用正则表达式", + allowableValues = ["true", "false"], + defaultValue = "false", + example = "false" + ) + val searchRegex: Boolean = false, + + /** + * Review + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "审核状态过滤(多个使用逗号分隔)", + allowableValues = ["NONE", "PROCESSING", "REJECT", "PASS"], + example = "NONE,PASS" + ) + val review: String?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "平台过滤(多个使用逗号分隔)", + allowableValues = ["WEB", "DESKTOP", "ANDROID"], + example = "WEB" + ) + val platform: String? +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementPassParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementPassParam.kt new file mode 100644 index 0000000..bf993c1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolManagementPassParam.kt @@ -0,0 +1,22 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank + +/** + * Pass tool parameters in tool management + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class ToolManagementPassParam( + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "产物") + @field:NotBlank(message = "Dist can not be blank") + val dist: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolStoreGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolStoreGetParam.kt new file mode 100644 index 0000000..5e645af --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolStoreGetParam.kt @@ -0,0 +1,36 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get tool parameters in tool store + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Trim +data class ToolStoreGetParam( + /** + * Value to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "查询内容", example = "ToolName") + var searchValue: String?, + + /** + * Platform to search for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "指定平台", example = "DESKTOP") + val platform: Platform? +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateAddParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateAddParam.kt new file mode 100644 index 0000000..c8560d1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateAddParam.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Add tool template parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolTemplateAddParam( + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称", required = true) + @field: NotBlank(message = "Name can not be blank") + var name: String?, + + /** + * Base ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Base ID", required = true) + @field: NotNull(message = "BaseId can not be null") + val baseId: Long? = null, + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "入口文件", required = true) + @field:NotBlank(message = "EntryPoint can not be null") + var entryPoint: String? = null, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"], defaultValue = "true") + val enable: Boolean = true +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateGetParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateGetParam.kt new file mode 100644 index 0000000..79e2485 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateGetParam.kt @@ -0,0 +1,27 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Get tool template parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + */ +@Schema(description = "获取工具模板请求参数") +data class ToolTemplateGetParam( + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "平台过滤(多个使用逗号分隔)", + allowableValues = ["WEB", "DESKTOP", "ANDROID"], + example = "WEB" + ) + val platform: String? +) : PageSortParam() diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateUpdateParam.kt new file mode 100644 index 0000000..1f7c232 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolTemplateUpdateParam.kt @@ -0,0 +1,66 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update tool template parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +data class ToolTemplateUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "ID", required = true) + @field: NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称") + @field: Pattern(regexp = "^.*\\S.*$", message = "Name can not be blank") + var name: String?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "源码") + @field: Pattern(regexp = "^.*\\S.*$", message = "Source can not be blank") + val source: String?, + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "入口文件") + @field: Pattern(regexp = "^.*\\S.*$", message = "Entry point can not be blank") + var entryPoint: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", allowableValues = ["true", "false"]) + val enable: Boolean? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpdateParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpdateParam.kt new file mode 100644 index 0000000..8e59883 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpdateParam.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import jakarta.validation.constraints.Size +import top.fatweb.oxygen.api.annotation.Trim + +/** + * Update tool parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "更新工具请求参数") +data class ToolUpdateParam( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "ID", required = true) + @field: NotNull(message = "ID can not be null") + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "名称") + @field: Pattern(regexp = "^.*\\S.*$", message = "Name can not be blank") + var name: String?, + + /** + * Icon + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "图标") + @field: Pattern(regexp = "^.*\\S.*$", message = "Icon can not be blank") + val icon: String?, + + /** + * Description + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "简介") + val description: String?, + + /** + * Keywords + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "关键词") + @field:Size(min = 1, message = "Keywords can not be empty") + val keywords: List?, + + /** + * Categories + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "类别") + @field:Size(min = 1, message = "Categories can not be empty") + val categories: List?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "源码") + @field: Pattern(regexp = "^.*\\S.*$", message = "Source can not be blank") + val source: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpgradeParam.kt b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpgradeParam.kt new file mode 100644 index 0000000..d4d5b61 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/param/tool/ToolUpgradeParam.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.param.tool + +import io.swagger.v3.oas.annotations.media.Schema +import jakarta.validation.constraints.NotBlank +import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.Pattern +import top.fatweb.oxygen.api.annotation.Trim +import top.fatweb.oxygen.api.entity.tool.Platform + +/** + * Upgrade tool parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Trim +@Schema(description = "升级工具请求参数") +data class ToolUpgradeParam( + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Trim + @Schema(description = "工具唯一 ID", required = true, example = "tool_a") + @field: NotBlank(message = "ToolId can not be blank") + @field: Pattern( + regexp = "^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$", + message = "ToolId can only match '^[a-zA-Z-_][0-9a-zA-Z-_]{2,19}\$'" + ) + var toolId: String?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "平台") + @field:NotNull(message = "Platform can not be null") + val platform: Platform?, + + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "版本", required = true, example = "1.0.3") + @field: NotBlank(message = "Ver can not be blank") + @field: Pattern(regexp = "^\\d+\\.\\d+\\.\\d+\$", message = "Ver can only match '..'") + val ver: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/properties/AdminProperties.kt b/src/main/kotlin/top/fatweb/oxygen/api/properties/AdminProperties.kt new file mode 100644 index 0000000..4638122 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/properties/AdminProperties.kt @@ -0,0 +1,46 @@ +package top.fatweb.oxygen.api.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.stereotype.Component + +/** + * Admin properties + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Component +@ConfigurationProperties("app.admin") +object AdminProperties { + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var username = "admin" + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var password: String? = null + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var nickname = "Administrator" + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var email = "admin@mail.com" +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/properties/FlywayProperties.kt b/src/main/kotlin/top/fatweb/oxygen/api/properties/FlywayProperties.kt new file mode 100644 index 0000000..c6f857e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/properties/FlywayProperties.kt @@ -0,0 +1,95 @@ +package top.fatweb.oxygen.api.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.stereotype.Component + +/** + * Flyway properties + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Component +@ConfigurationProperties("spring.flyway") +object FlywayProperties { + /** + * Locations of migrations scripts. Can contain the special "{vendor}" placeholder to + * use vendor-specific locations. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var locations = listOf("classpath:db/migration") + + /** + * Name of the schema history table that will be used by Flyway. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var table = "flyway_schema_history" + + /** + * Whether to allow migrations to be run out of order. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var outOfOrder = false + + /** + * Whether to automatically call validate when performing a migration. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var validateOnMigrate = true + + /** + * Encoding of SQL migrations. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var encoding = Charsets.UTF_8 + + /** + * File name prefix for SQL migrations. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var sqlMigrationPrefix = "V" + + /** + * File name separator for SQL migrations. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var sqlMigrationSeparator = "__" + + /** + * File name suffix for SQL migrations. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var sqlMigrationSuffixes = listOf(".sql") + + /** + * Whether to automatically call baseline when migrating a non-empty schema. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var baselineOnMigrate = true + + /** + * Version to tag an existing schema with when executing baseline. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var baselineVersion = "0" +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/properties/SecurityProperties.kt b/src/main/kotlin/top/fatweb/oxygen/api/properties/SecurityProperties.kt new file mode 100644 index 0000000..fe70e6d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/properties/SecurityProperties.kt @@ -0,0 +1,79 @@ +package top.fatweb.oxygen.api.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.stereotype.Component +import java.util.concurrent.TimeUnit + +/** + * Security properties + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Component +@ConfigurationProperties("app.security") +object SecurityProperties { + /** + * Key to get authentication from header + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var headerKey = "Authorization" + + /** + * Prefix of token + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var tokenPrefix = "Bearer " + + /** + * TTL of JWT + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var jwtTtl = 2L + + /** + * TTL unit of JWT + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var jwtTtlUnit = TimeUnit.HOURS + + /** + * Key of JWT + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var jwtKey = "Oxygen" + + /** + * Issuer of JWT + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var jwtIssuer = "Oxygen" + + /** + * TTL of redis + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var redisTtl = 20L + + /** + * TTL unit of redis + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var redisTtlUnit = TimeUnit.MINUTES +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/properties/ServerProperties.kt b/src/main/kotlin/top/fatweb/oxygen/api/properties/ServerProperties.kt new file mode 100644 index 0000000..83b2835 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/properties/ServerProperties.kt @@ -0,0 +1,61 @@ +package top.fatweb.oxygen.api.properties + +import org.springframework.boot.context.properties.ConfigurationProperties +import org.springframework.stereotype.Component +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.ZoneOffset +import java.time.ZonedDateTime + +/** + * Application properties + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Component +@ConfigurationProperties("app") +object ServerProperties { + /** + * App name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + lateinit var appName: String + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + lateinit var version: String + + /** + * Build time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + lateinit var buildTime: String + + /** + * Startup time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + val startupTime: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC) + + /** + * Turnstile secret key + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + lateinit var turnstileSecretKey: String + + fun buildZoneDateTime(zoneId: ZoneId = ZoneId.systemDefault()): ZonedDateTime = + LocalDateTime.parse(buildTime).atZone(ZoneId.of("UTC")).withZoneSameInstant(zoneId) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/IAvatarService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/IAvatarService.kt new file mode 100644 index 0000000..46520c9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/IAvatarService.kt @@ -0,0 +1,125 @@ +package top.fatweb.oxygen.api.service.api.v1 + +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarBaseParam +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarGitHubParam +import top.fatweb.oxygen.api.vo.api.v1.avatar.AvatarBase64Vo + +/** + * Avatar service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface IAvatarService { + /** + * Generate random avatar + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun random(avatarBaseParam: AvatarBaseParam?): ByteArray + + /** + * Generate random avatar as base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun randomBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo + + /** + * Generate triangle style avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + fun triangle(avatarBaseParam: AvatarBaseParam?): ByteArray + + /** + * Generate triangle style avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return AvatarBase64Vo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see AvatarBase64Vo + */ + fun triangleBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo + + /** + * Generate square style avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + fun square(avatarBaseParam: AvatarBaseParam?): ByteArray + + /** + * Generate square style avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return AvatarBase64Vo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see AvatarBase64Vo + */ + fun squareBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo + + /** + * Generate identicon style avatar + * + * @param avatarBaseParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + fun identicon(avatarBaseParam: AvatarBaseParam?): ByteArray + + /** + * Generate identicon style avatar as base64 + * + * @param avatarBaseParam Avatar base parameters + * @return AvatarBase64Vo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see AvatarBase64Vo + */ + fun identiconBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo + + /** + * Generate GitHub style avatar + * + * @param avatarGitHubParam Avatar base parameters + * @return Avatar byte array + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see ByteArray + */ + fun github(avatarGitHubParam: AvatarGitHubParam?): ByteArray + + /** + * Generate GitHub style avatar as base64 + * + * @param avatarGitHubParam Avatar base parameters + * @return AvatarBase64Vo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see AvatarBaseParam + * @see AvatarBase64Vo + */ + fun githubBase64(avatarGitHubParam: AvatarGitHubParam?): AvatarBase64Vo +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/impl/AvatarServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/impl/AvatarServiceImpl.kt new file mode 100644 index 0000000..ddb2de5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/api/v1/impl/AvatarServiceImpl.kt @@ -0,0 +1,144 @@ +package top.fatweb.oxygen.api.service.api.v1.impl + +import org.springframework.stereotype.Service +import top.fatweb.avatargenerator.GitHubAvatar +import top.fatweb.avatargenerator.IdenticonAvatar +import top.fatweb.avatargenerator.SquareAvatar +import top.fatweb.avatargenerator.TriangleAvatar +import top.fatweb.avatargenerator.layer.background.ColorPaintBackgroundLayer +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarBaseParam +import top.fatweb.oxygen.api.param.api.v1.avatar.AvatarGitHubParam +import top.fatweb.oxygen.api.service.api.v1.IAvatarService +import top.fatweb.oxygen.api.util.NumberUtil +import top.fatweb.oxygen.api.vo.api.v1.avatar.AvatarBase64Vo +import java.awt.Color +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi + +/** + * Avatar service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IAvatarService + */ +@OptIn(ExperimentalEncodingApi::class) +@Service +class AvatarServiceImpl : IAvatarService { + override fun random(avatarBaseParam: AvatarBaseParam?): ByteArray = + when ((1..4).random()) { + 1 -> triangle(avatarBaseParam) + 2 -> square(avatarBaseParam) + 3 -> identicon(avatarBaseParam) + else -> github(AvatarGitHubParam().apply { + seed = avatarBaseParam?.seed + size = avatarBaseParam?.size + margin = avatarBaseParam?.margin + padding = avatarBaseParam?.padding + colors = avatarBaseParam?.colors + background = avatarBaseParam?.background + }) + } + + override fun randomBase64(avatarBaseParam: AvatarBaseParam?): AvatarBase64Vo = when ((1..4).random()) { + 1 -> triangleBase64(avatarBaseParam) + 2 -> squareBase64(avatarBaseParam) + 3 -> identiconBase64(avatarBaseParam) + else -> githubBase64(AvatarGitHubParam().apply { + seed = avatarBaseParam?.seed + size = avatarBaseParam?.size + margin = avatarBaseParam?.margin + padding = avatarBaseParam?.padding + colors = avatarBaseParam?.colors + background = avatarBaseParam?.background + }) + } + + override fun triangle(avatarBaseParam: AvatarBaseParam?): ByteArray { + val avatar = ( + if (avatarBaseParam == null || avatarBaseParam.colors.isNullOrEmpty()) + TriangleAvatar.newAvatarBuilder() + else TriangleAvatar.newAvatarBuilder( + *avatarBaseParam.colors!!.map(::decodeColor).toTypedArray() + ) + ).apply { + avatarBaseParam?.size?.let(::size) + avatarBaseParam?.margin?.let(::margin) + avatarBaseParam?.padding?.let(::padding) + avatarBaseParam?.background?.let { layers(ColorPaintBackgroundLayer(decodeColor(it))) } + }.build() + + return avatar.createAsPngBytes(avatarBaseParam?.seed ?: NumberUtil.getRandomLong()) + } + + override fun triangleBase64(avatarBaseParam: AvatarBaseParam?) = + AvatarBase64Vo(Base64.encode(triangle(avatarBaseParam))) + + override fun square(avatarBaseParam: AvatarBaseParam?): ByteArray { + val avatar = ( + if (avatarBaseParam == null || avatarBaseParam.colors.isNullOrEmpty()) + SquareAvatar.newAvatarBuilder() + else SquareAvatar.newAvatarBuilder( + *avatarBaseParam.colors!!.map(::decodeColor).toTypedArray() + ) + ).apply { + avatarBaseParam?.size?.let(::size) + avatarBaseParam?.margin?.let(::margin) + avatarBaseParam?.padding?.let(::padding) + avatarBaseParam?.background?.let { layers(ColorPaintBackgroundLayer(decodeColor(it))) } + }.build() + + return avatar.createAsPngBytes(avatarBaseParam?.seed ?: NumberUtil.getRandomLong()) + } + + override fun squareBase64(avatarBaseParam: AvatarBaseParam?) = + AvatarBase64Vo(Base64.encode(square(avatarBaseParam))) + + override fun identicon(avatarBaseParam: AvatarBaseParam?): ByteArray { + val avatar = IdenticonAvatar.newAvatarBuilder().apply { + avatarBaseParam?.size?.let(::size) + avatarBaseParam?.margin?.let(::margin) + avatarBaseParam?.padding?.let(::padding) + if (avatarBaseParam != null && !avatarBaseParam.colors.isNullOrEmpty()) { + color(decodeColor(avatarBaseParam.colors!!.random())) + } + avatarBaseParam?.background?.let { layers(ColorPaintBackgroundLayer(decodeColor(it))) } + }.build() + + return avatar.createAsPngBytes(avatarBaseParam?.seed ?: NumberUtil.getRandomLong()) + } + + override fun identiconBase64(avatarBaseParam: AvatarBaseParam?) = + AvatarBase64Vo(Base64.encode(identicon(avatarBaseParam))) + + override fun github(avatarGitHubParam: AvatarGitHubParam?): ByteArray { + val avatar = (avatarGitHubParam?.let { GitHubAvatar.newAvatarBuilder(it.elementSize, it.precision) } + ?: let { GitHubAvatar.newAvatarBuilder(400, 5) }).apply { + avatarGitHubParam?.size?.let(::size) + avatarGitHubParam?.margin?.let(::margin) + avatarGitHubParam?.padding?.let(::padding) + if (avatarGitHubParam != null && !avatarGitHubParam.colors.isNullOrEmpty()) { + color(decodeColor(avatarGitHubParam.colors!!.random())) + } + avatarGitHubParam?.background?.let { layers(ColorPaintBackgroundLayer(decodeColor(it))) } + }.build() + + return avatar.createAsPngBytes(avatarGitHubParam?.seed ?: NumberUtil.getRandomLong()) + } + + override fun githubBase64(avatarGitHubParam: AvatarGitHubParam?) = + AvatarBase64Vo(Base64.encode(github(avatarGitHubParam))) + + fun decodeColor(nm: String): Color { + return if (Regex("^#[0-9a-fA-F]{6}$").matches(nm)) { + Color.decode(nm) + } else if (Regex("^#[0-9a-fA-F]{8}$").matches(nm)) { + val intVal = Integer.decode(nm.substring(1..6).prependIndent("#")) + val alpha = Integer.decode(nm.substring(7).prependIndent("#")) + Color(intVal shr 16 and 0xFF, intVal shr 8 and 0XFF, intVal and 0xFF, alpha and 0xFF) + } else { + Color.WHITE + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IAuthenticationService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IAuthenticationService.kt new file mode 100644 index 0000000..d5e8ad9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IAuthenticationService.kt @@ -0,0 +1,138 @@ +package top.fatweb.oxygen.api.service.permission + +import jakarta.servlet.http.HttpServletRequest +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.param.permission.* +import top.fatweb.oxygen.api.vo.permission.LoginVo +import top.fatweb.oxygen.api.vo.permission.RegisterVo +import top.fatweb.oxygen.api.vo.permission.TokenVo +import top.fatweb.oxygen.api.vo.permission.TwoFactorVo + +/** + * Authentication service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface IAuthenticationService { + /** + * Register + * + * @param registerParam Register parameters + * @return RegisterVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RegisterParam + * @see RegisterVo + */ + fun register(request: HttpServletRequest, registerParam: RegisterParam): RegisterVo + + /** + * Send verify email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun resend() + + /** + * Verify email + * + * @param verifyParam Verify parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see VerifyParam + */ + fun verify(verifyParam: VerifyParam) + + /** + * Forget password + * + * @param request + * @param forgetParam Forget parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see ForgetParam + */ + fun forget(request: HttpServletRequest, forgetParam: ForgetParam) + + /** + * Retrieve password + * + * @param request + * @param retrieveParam Retrieve parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see RetrieveParam + */ + fun retrieve(request: HttpServletRequest, retrieveParam: RetrieveParam) + + /** + * Login + * + * @param request + * @param loginParam Login parameters + * @return LoginVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + * @see User + * @see LoginVo + */ + fun login(request: HttpServletRequest, loginParam: LoginParam): LoginVo + + /** + * Create two-factor + * + * @return Two-factor QR code + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorVo + */ + fun createTwoFactor(): TwoFactorVo + + /** + * Validate two-factor + * + * @param twoFactorValidateParam Validate two-factor parameters + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorValidateParam + */ + fun validateTwoFactor(twoFactorValidateParam: TwoFactorValidateParam): Boolean + + /** + * Remove two-factor + * + * @param twoFactorRemoveParam Remove two-factor parameters + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorRemoveParam + */ + fun removeTwoFactor(twoFactorRemoveParam: TwoFactorRemoveParam): Boolean + + /** + * Logout + * + * @param token Token + * @return Logout result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun logout(token: String): Boolean + + /** + * Renew token + * + * @param token Token + * @return TokenVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TokenVo + */ + fun renewToken(token: String): TokenVo +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IFuncService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IFuncService.kt new file mode 100644 index 0000000..36af68a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IFuncService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Func + +/** + * Function service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Func + */ +interface IFuncService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IGroupService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IGroupService.kt new file mode 100644 index 0000000..9db4c4f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IGroupService.kt @@ -0,0 +1,106 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Group +import top.fatweb.oxygen.api.param.permission.group.* +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo +import top.fatweb.oxygen.api.vo.permission.base.GroupVo + +/** + * Group service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Group + */ +interface IGroupService : IService { + /** + * Get one group by ID + * + * @param id ID + * @return GroupWithRoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupWithRoleVo + */ + fun getOne(id: Long): GroupWithRoleVo + + /** + * Get group in page + * + * @param groupGetParam Get group parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupGetParam + * @see PageVo + * @see GroupWithRoleVo + */ + fun getPage(groupGetParam: GroupGetParam?): PageVo + + /** + * Get all group in list + * + * @return List of GroupVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupVo + */ + fun getList(): List + + /** + * Add group + * + * @param groupAddParam Add group parameters + * @return GroupVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupAddParam + * @see GroupVo + */ + fun add(groupAddParam: GroupAddParam): GroupVo + + /** + * Update group + * + * @param groupUpdateParam Update group parameters + * @return GroupVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateParam + * @see GroupVo + */ + fun update(groupUpdateParam: GroupUpdateParam): GroupVo + + /** + * Update status of group + * + * @param groupUpdateStatusParam Update status of group parameters + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupUpdateStatusParam + */ + fun status(groupUpdateStatusParam: GroupUpdateStatusParam) + + /** + * Delete group by ID + * + * @param id ID + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun deleteOne(id: Long) + + /** + * Delete group by list + * + * @param groupDeleteParam Delete group parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupDeleteParam + */ + fun delete(groupDeleteParam: GroupDeleteParam) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IMenuService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IMenuService.kt new file mode 100644 index 0000000..967d56a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IMenuService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Menu + +/** + * Menu service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Menu + */ +interface IMenuService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IModuleService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IModuleService.kt new file mode 100644 index 0000000..431a84d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IModuleService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Module + +/** + * Module service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Module + */ +interface IModuleService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IOperationService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IOperationService.kt new file mode 100644 index 0000000..a468550 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IOperationService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Operation + +/** + * Operation service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Operation + */ +interface IOperationService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IPowerService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IPowerService.kt new file mode 100644 index 0000000..b5a34cd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IPowerService.kt @@ -0,0 +1,25 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Power +import top.fatweb.oxygen.api.vo.permission.PowerSetVo + +/** + * Power service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Power + */ +interface IPowerService : IService { + /** + * Get all power as list + * + * @return powerSetVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PowerSetVo + */ + fun getList(): PowerSetVo +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRPowerRoleService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRPowerRoleService.kt new file mode 100644 index 0000000..9984d69 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRPowerRoleService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.RPowerRole + +/** + * Power role intermediate service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see RPowerRole + */ +interface IRPowerRoleService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRRoleGroupService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRRoleGroupService.kt new file mode 100644 index 0000000..f536395 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRRoleGroupService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.RRoleGroup + +/** + * Role group intermediate service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see RRoleGroup + */ +interface IRRoleGroupService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserGroupService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserGroupService.kt new file mode 100644 index 0000000..008aa4c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserGroupService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.RUserGroup + +/** + * User group intermediate service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see RUserGroup + */ +interface IRUserGroupService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserRoleService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserRoleService.kt new file mode 100644 index 0000000..969f3ee --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRUserRoleService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.RUserRole + +/** + * User role intermediate service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see RUserRole + */ +interface IRUserRoleService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRoleService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRoleService.kt new file mode 100644 index 0000000..37d6da7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IRoleService.kt @@ -0,0 +1,106 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.Role +import top.fatweb.oxygen.api.param.permission.role.* +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo + +/** + * Role service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Role + */ +interface IRoleService : IService { + /** + * Get user by ID + * + * @param id ID + * @return RoleWithPowerVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleWithPowerVo + */ + fun getOne(id: Long): RoleWithPowerVo + + /** + * Get role in page + * + * @param roleGetParam Get role parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleGetParam + * @see PageVo + * @see RoleWithPowerVo + */ + fun getPage(roleGetParam: RoleGetParam?): PageVo + + /** + * Get all role in list + * + * @return List of RoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleVo + */ + fun getList(): List + + /** + * Add role + * + * @param roleAddParam Add role parameters + * @return RoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleAddParam + * @see RoleVo + */ + fun add(roleAddParam: RoleAddParam): RoleVo + + /** + * Update role + * + * @param roleUpdateParam Update role parameters + * @return RoleVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateParam + * @see RoleVo + */ + fun update(roleUpdateParam: RoleUpdateParam): RoleVo + + /** + * Update status of role + * + * @param roleUpdateStatusParam Update status of role parameters + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleUpdateStatusParam + */ + fun status(roleUpdateStatusParam: RoleUpdateStatusParam) + + /** + * Delete role by ID + * + * @param id ID + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun deleteOne(id: Long) + + /** + * Delete role by list + * + * @param roleDeleteParam Delete role parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleDeleteParam + */ + fun delete(roleDeleteParam: RoleDeleteParam) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserInfoService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserInfoService.kt new file mode 100644 index 0000000..c2b29f3 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserInfoService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.UserInfo + +/** + * User information service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see UserInfo + */ +interface IUserInfoService : IService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserService.kt new file mode 100644 index 0000000..f7b2475 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/IUserService.kt @@ -0,0 +1,175 @@ +package top.fatweb.oxygen.api.service.permission + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.param.permission.user.* +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo + +/** + * User service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see User + */ +interface IUserService : IService { + /** + * Get user with power by username or email + * + * @param account Username or email + * @return User object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see User + */ + fun getUserWithPowerByAccount(account: String): User? + + /** + * Get user information + * + * @return UserWithPowerInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserWithPowerInfoVo + */ + fun getInfo(): UserWithPowerInfoVo + + /** + * Get user information by username + * + * @param username Username + * @return UserWithInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserWithInfoVo + */ + fun getBasicInfo(username: String): UserWithInfoVo + + /** + * Update user information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun updateInfo(userInfoUpdateParam: UserInfoUpdateParam): Boolean + + /** + * Change password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun password(userChangePasswordParam: UserChangePasswordParam) + + /** + * Get one user by ID + * + * @param id ID + * @return UserWithRoleInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserWithRoleInfoVo + */ + fun getOne(id: Long): UserWithRoleInfoVo + + /** + * Get user in page + * + * @param userGetParam Get user parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserGetParam + * @see PageVo + * @see UserWithRoleInfoVo + */ + fun getPage(userGetParam: UserGetParam?): PageVo + + /** + * Get all user as list + * + * @return List of UserWithInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserWithInfoVo + */ + fun getList(): List + + /** + * Add user + * + * @param userAddParam Add user parameters + * @return UserWithPasswordRoleInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserAddParam + * @see UserWithPasswordRoleInfoVo + */ + fun add(userAddParam: UserAddParam): UserWithPasswordRoleInfoVo + + /** + * Update user + * + * @param userUpdateParam Update user parameters + * @return UserWithRoleInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserUpdateParam + * @see UserWithRoleInfoVo + */ + fun update(userUpdateParam: UserUpdateParam): UserWithRoleInfoVo + + /** + * Update user password + * + * @param userUpdatePasswordParam Update user password parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserUpdatePasswordParam + */ + fun password(userUpdatePasswordParam: UserUpdatePasswordParam) + + /** + * Delete user by ID + * + * @param id ID + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun deleteOne(id: Long) + + /** + * Delete user by list + * + * @param userDeleteParam Delete user parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserDeleteParam + */ + fun delete(userDeleteParam: UserDeleteParam) + + /** + * Get user IDs list by list of role IDs + * + * @param roleIds List of role IDs + * @return User IDs list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getIdsByRoleIds(roleIds: List): List + + /** + * Get user IDs list by list of group IDs + * + * @param groupIds List of group IDs + * @return User IDs list + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getIdsByGroupIds(groupIds: List): List +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/AuthenticationServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/AuthenticationServiceImpl.kt new file mode 100644 index 0000000..fdb418c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/AuthenticationServiceImpl.kt @@ -0,0 +1,415 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import jakarta.servlet.http.HttpServletRequest +import org.apache.velocity.VelocityContext +import org.apache.velocity.app.VelocityEngine +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.security.access.AccessDeniedException +import org.springframework.security.authentication.AuthenticationManager +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.annotation.EventLogRecord +import top.fatweb.oxygen.api.entity.permission.LoginUser +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.entity.system.EventLog +import top.fatweb.oxygen.api.exception.* +import top.fatweb.oxygen.api.http.TurnstileApi +import top.fatweb.oxygen.api.param.permission.* +import top.fatweb.oxygen.api.properties.SecurityProperties +import top.fatweb.oxygen.api.service.api.v1.IAvatarService +import top.fatweb.oxygen.api.service.permission.IAuthenticationService +import top.fatweb.oxygen.api.service.permission.IUserInfoService +import top.fatweb.oxygen.api.service.permission.IUserService +import top.fatweb.oxygen.api.service.system.ISensitiveWordService +import top.fatweb.oxygen.api.settings.BaseSettings +import top.fatweb.oxygen.api.settings.SettingsOperator +import top.fatweb.oxygen.api.settings.TwoFactorSettings +import top.fatweb.oxygen.api.util.* +import top.fatweb.oxygen.api.vo.permission.LoginVo +import top.fatweb.oxygen.api.vo.permission.RegisterVo +import top.fatweb.oxygen.api.vo.permission.TokenVo +import top.fatweb.oxygen.api.vo.permission.TwoFactorVo +import java.io.StringWriter +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.util.* + +/** + * Authentication service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see VelocityEngine + * @see AuthenticationManager + * @see PasswordEncoder + * @see RedisUtil + * @see TurnstileApi + * @see IUserService + * @see IUserInfoService + * @see ISensitiveWordService + * @see IAvatarService + * @see IAuthenticationService + */ +@Service +class AuthenticationServiceImpl( + private val velocityEngine: VelocityEngine, + private val authenticationManager: AuthenticationManager, + private val passwordEncoder: PasswordEncoder, + private val redisUtil: RedisUtil, + private val turnstileApi: TurnstileApi, + private val userService: IUserService, + private val userInfoService: IUserInfoService, + private val sensitiveWordService: ISensitiveWordService, + private val avatarService: IAvatarService +) : IAuthenticationService { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + @EventLogRecord(EventLog.Event.REGISTER) + @Transactional + override fun register(request: HttpServletRequest, registerParam: RegisterParam): RegisterVo { + verifyCaptcha(registerParam.captchaCode!!) + sensitiveWordService.checkSensitiveWord(registerParam.username!!) + + val user = User().apply { + username = registerParam.username + password = passwordEncoder.encode(registerParam.password) + verify = + "${ + LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli() + }-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}" + locking = 0 + enable = 1 + } + userService.save(user) + userInfoService.save(UserInfo().apply { + userId = user.id + nickname = registerParam.username + avatar = avatarService.randomBase64(null).base64 + email = registerParam.email + }) + + sendVerifyMail(user.username!!, user.verify!!, registerParam.email!!) + + val loginVo = this.login(request, registerParam.username!!, registerParam.password!!) + + return RegisterVo(token = loginVo.token, userId = loginVo.userId) + } + + @Transactional + override fun resend() { + val user = userService.getById(WebUtil.getLoginUserId()) + + user.verify ?: throw NoVerificationRequiredException() + + if (LocalDateTime.ofInstant(Instant.ofEpochMilli(user.verify!!.split("-").first().toLong()), ZoneOffset.UTC) + .isAfter(LocalDateTime.now(ZoneOffset.UTC).minusMinutes(5)) + ) { + throw RequestTooFrequentException() + } + + user.verify = + "${ + LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli() + }-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}" + user.updateTime = LocalDateTime.now(ZoneOffset.UTC) + userService.updateById(user) + + WebUtil.getLoginUser()?.user?.userInfo?.email?.let { + sendVerifyMail(user.username!!, user.verify!!, it) + } ?: throw AccessDeniedException("Access Denied") + } + + @EventLogRecord(EventLog.Event.VERIFY) + @Transactional + override fun verify(verifyParam: VerifyParam) { + val user = userService.getById(WebUtil.getLoginUserId()) + user.verify ?: throw NoVerificationRequiredException() + if (LocalDateTime.ofInstant(Instant.ofEpochMilli(user.verify!!.split("-").first().toLong()), ZoneOffset.UTC) + .isBefore(LocalDateTime.now(ZoneOffset.UTC).minusHours(2)) || user.verify != verifyParam.code + ) { + throw VerificationCodeErrorOrExpiredException() + } + + if (verifyParam.nickname.isNullOrBlank() || verifyParam.avatar.isNullOrBlank()) { + throw AccountNeedInitException() + } + sensitiveWordService.checkSensitiveWord(verifyParam.nickname!!) + + userService.update( + KtUpdateWrapper(User()).eq(User::id, user.id).set(User::verify, null) + .set(User::updateTime, LocalDateTime.now(ZoneOffset.UTC)) + ) + userInfoService.update( + KtUpdateWrapper(UserInfo()).eq(UserInfo::userId, user.id).set(UserInfo::nickname, verifyParam.nickname) + .set(UserInfo::avatar, verifyParam.avatar) + ) + } + + @Transactional + override fun forget(request: HttpServletRequest, forgetParam: ForgetParam) { + verifyCaptcha(forgetParam.captchaCode!!) + + val user = userService.getUserWithPowerByAccount(forgetParam.email!!) + user ?: throw UserNotFoundException() + + user.forget?.let { + if (LocalDateTime.ofInstant(Instant.ofEpochMilli(it.split("-").first().toLong()), ZoneOffset.UTC) + .isAfter(LocalDateTime.now(ZoneOffset.UTC).minusMinutes(5)) + ) { + throw RequestTooFrequentException() + } + } + + val code = "${ + LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli() + }-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}" + userService.update(KtUpdateWrapper(User()).eq(User::id, user.id).set(User::forget, code)) + sendRetrieveMail(user.username!!, WebUtil.getRequestIp(request), code, forgetParam.email!!) + } + + @Transactional + override fun retrieve(request: HttpServletRequest, retrieveParam: RetrieveParam) { + verifyCaptcha(retrieveParam.captchaCode!!) + + val codeStrings = retrieveParam.code!!.split("-") + if (codeStrings.size != 16) { + throw RetrieveCodeErrorOrExpiredException() + } + try { + if (LocalDateTime.ofInstant(Instant.ofEpochMilli(codeStrings.first().toLong()), ZoneOffset.UTC) + .isBefore(LocalDateTime.now(ZoneOffset.UTC).minusHours(2)) + ) { + throw RetrieveCodeErrorOrExpiredException() + } + } catch (e: Exception) { + throw RetrieveCodeErrorOrExpiredException() + } + + val user = userService.getOne(KtQueryWrapper(User()).eq(User::forget, retrieveParam.code)) + ?: throw RetrieveCodeErrorOrExpiredException() + val userInfo = userInfoService.getOne(KtQueryWrapper(UserInfo()).eq(UserInfo::userId, user.id)) + + userService.update( + KtUpdateWrapper(User()).eq(User::id, user.id).set(User::forget, null) + .set(User::password, passwordEncoder.encode(retrieveParam.password!!)) + ) + + WebUtil.offlineUser(redisUtil, user.id!!) + + sendPasswordChangedMail(user.username!!, WebUtil.getRequestIp(request), userInfo!!.email!!) + } + + @EventLogRecord(EventLog.Event.LOGIN) + override fun login(request: HttpServletRequest, loginParam: LoginParam): LoginVo { + if (loginParam.twoFactorCode.isNullOrBlank()) { + verifyCaptcha(loginParam.captchaCode!!) + } + + return this.login(request, loginParam.account!!, loginParam.password!!, loginParam.twoFactorCode) + } + + override fun createTwoFactor(): TwoFactorVo { + val user = userService.getById(WebUtil.getLoginUserId()) ?: throw UserNotFoundException() + + if (!user.twoFactor.isNullOrBlank() && !user.twoFactor!!.endsWith("?")) { + throw AlreadyHasTwoFactorException() + } + + val secretKey = TOTPUtil.generateSecretKey(SettingsOperator.getTwoFactorValue(TwoFactorSettings::secretKeyLength, 16)) + val qrCodeSVGBase64 = TOTPUtil.generateQRCodeSVGBase64( + SettingsOperator.getTwoFactorValue(TwoFactorSettings::issuer, "OxygenToolbox"), + user.username!!, + secretKey + ) + + userService.update(KtUpdateWrapper(User()).eq(User::id, user.id).set(User::twoFactor, "${secretKey}?")) + + return TwoFactorVo(qrCodeSVGBase64) + } + + override fun validateTwoFactor(twoFactorValidateParam: TwoFactorValidateParam): Boolean { + val user = userService.getById(WebUtil.getLoginUserId()) ?: throw UserNotFoundException() + if (user.twoFactor.isNullOrBlank()) { + throw NoTwoFactorFoundException() + } + if (!user.twoFactor!!.endsWith("?")) { + throw AlreadyHasTwoFactorException() + } + val secretKey = user.twoFactor!!.substring(0, user.twoFactor!!.length - 1) + + if (TOTPUtil.validateCode(secretKey, twoFactorValidateParam.code!!)) { + userService.update(KtUpdateWrapper(User()).eq(User::id, user.id).set(User::twoFactor, secretKey)) + return true + } + + return false + } + + override fun removeTwoFactor(twoFactorRemoveParam: TwoFactorRemoveParam): Boolean { + val user = userService.getById(WebUtil.getLoginUserId()) ?: throw UserNotFoundException() + if (user.twoFactor.isNullOrBlank() || user.twoFactor!!.endsWith("?")) { + throw NoTwoFactorFoundException() + } + + if (TOTPUtil.validateCode(user.twoFactor!!, twoFactorRemoveParam.code!!)) { + userService.update(KtUpdateWrapper(User()).eq(User::id, user.id).set(User::twoFactor, null)) + return true + } + + return false + } + + @EventLogRecord(EventLog.Event.LOGOUT) + override fun logout(token: String): Boolean { + val loginUser = WebUtil.getLoginUser() ?: throw TokenHasExpiredException() + + return redisUtil.delObject("${SecurityProperties.jwtIssuer}_login_${loginUser.user.id}:${token}") + } + + override fun renewToken(token: String): TokenVo { + val loginUser = WebUtil.getLoginUser() ?: throw TokenHasExpiredException() + + val oldRedisKey = "${SecurityProperties.jwtIssuer}_login_${loginUser.user.id}:${token}" + redisUtil.delObject(oldRedisKey) + val jwt = JwtUtil.createJwt(WebUtil.getLoginUserId().toString()) + + jwt ?: throw RuntimeException("Login failed") + + val redisKey = "${SecurityProperties.jwtIssuer}_login_${loginUser.user.id}:${jwt}" + redisUtil.setObject( + redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit + ) + + return TokenVo(jwt) + } + + private fun sendVerifyMail(username: String, code: String, email: String) { + val velocityContext = VelocityContext().apply { + put("appName", SettingsOperator.getAppValue(BaseSettings::appName, "氧工具")) + put("appUrl", SettingsOperator.getAppValue(BaseSettings::appUrl, "http://localhost")) + put("username", username) + put( + "verifyUrl", + SettingsOperator.getAppValue(BaseSettings::verifyUrl, "http://localhost/verify?code=\${verifyCode}") + .replace( + Regex("(?<=([^\\\\]))\\$\\{verifyCode}"), code + ) + ) + } + val template = velocityEngine.getTemplate("templates/email-verify-account-cn.vm") + + val stringWriter = StringWriter() + template.merge(velocityContext, stringWriter) + + MailUtil.sendSimpleMail( + "验证您的账号", stringWriter.toString(), true, + email + ) + } + + private fun sendRetrieveMail(username: String, ip: String, code: String, email: String) { + val velocityContext = VelocityContext().apply { + put("appName", SettingsOperator.getAppValue(BaseSettings::appName, "氧工具")) + put("appUrl", SettingsOperator.getAppValue(BaseSettings::appUrl, "http://localhost")) + put("username", username) + put("ipAddress", ip) + put( + "retrieveUrl", + SettingsOperator.getAppValue( + BaseSettings::retrieveUrl, + "http://localhost/retrieve?code=\${retrieveCode}" + ) + .replace( + Regex("(?<=([^\\\\]))\\$\\{retrieveCode}"), code + ) + ) + } + val template = velocityEngine.getTemplate("templates/email-retrieve-password-cn.vm") + + val stringWriter = StringWriter() + template.merge(velocityContext, stringWriter) + + MailUtil.sendSimpleMail( + "找回您的密码", stringWriter.toString(), true, + email + ) + } + + private fun sendPasswordChangedMail(username: String, ip: String, email: String) { + val velocityContext = VelocityContext().apply { + put("appName", SettingsOperator.getAppValue(BaseSettings::appName, "氧工具")) + put("appUrl", SettingsOperator.getAppValue(BaseSettings::appUrl, "http://localhost")) + put("username", username) + put("ipAddress", ip) + } + val template = velocityEngine.getTemplate("templates/email-password-changed-cn.vm") + + val stringWriter = StringWriter() + template.merge(velocityContext, stringWriter) + + MailUtil.sendSimpleMail( + "您的密码已更改", stringWriter.toString(), true, + email + ) + } + + private fun login( + request: HttpServletRequest, + account: String, + password: String, + twoFactorCode: String? = null + ): LoginVo { + val usernamePasswordAuthenticationToken = + UsernamePasswordAuthenticationToken(account, password) + val authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken) + authentication ?: throw RuntimeException("Login failed") + + val loginUser = authentication.principal as LoginUser + loginUser.user.password = "" + + if (!loginUser.user.twoFactor.isNullOrBlank() && !loginUser.user.twoFactor!!.endsWith("?")) { + if (twoFactorCode.isNullOrBlank()) { + throw NeedTwoFactorException() + } + if (!TOTPUtil.validateCode(loginUser.user.twoFactor!!, twoFactorCode)) { + throw TwoFactorVerificationCodeErrorException() + } + } + + logger.info("用户登录 [用户名: '{}', IP: '{}']", loginUser.username, WebUtil.getRequestIp(request)) + userService.update(User().apply { + currentLoginIp = WebUtil.getRequestIp(request) + currentLoginTime = LocalDateTime.now(ZoneOffset.UTC) + lastLoginIp = loginUser.user.currentLoginIp + lastLoginTime = loginUser.user.currentLoginTime + }, KtUpdateWrapper(User()).eq(User::username, loginUser.username)) + + val userId = loginUser.user.id.toString() + val jwt = JwtUtil.createJwt(userId) + + jwt ?: throw RuntimeException("Login failed") + + val redisKey = "${SecurityProperties.jwtIssuer}_login_${userId}:${jwt}" + redisUtil.setObject(redisKey, loginUser, SecurityProperties.redisTtl, SecurityProperties.redisTtlUnit) + + return LoginVo(jwt, loginUser.user.id, loginUser.user.currentLoginTime, loginUser.user.currentLoginIp) + } + + private fun verifyCaptcha(captchaCode: String) { + try { + val siteverifyResponse = turnstileApi.siteverify(captchaCode) + if (!siteverifyResponse.success) { + throw InvalidCaptchaCodeException() + } + } catch (e: Exception) { + throw InvalidCaptchaCodeException() + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/FuncServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/FuncServiceImpl.kt new file mode 100644 index 0000000..cbfbba6 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/FuncServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.Func +import top.fatweb.oxygen.api.mapper.permission.FuncMapper +import top.fatweb.oxygen.api.service.permission.IFuncService + +/** + * Function service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see FuncMapper + * @see Func + * @see IFuncService + */ +@Service +class FuncServiceImpl : ServiceImpl(), IFuncService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/GroupServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/GroupServiceImpl.kt new file mode 100644 index 0000000..5a61ce4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/GroupServiceImpl.kt @@ -0,0 +1,154 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.permission.GroupConverter +import top.fatweb.oxygen.api.entity.permission.Group +import top.fatweb.oxygen.api.entity.permission.RRoleGroup +import top.fatweb.oxygen.api.exception.DatabaseInsertException +import top.fatweb.oxygen.api.exception.DatabaseUpdateException +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.mapper.permission.GroupMapper +import top.fatweb.oxygen.api.param.permission.group.* +import top.fatweb.oxygen.api.service.permission.IGroupService +import top.fatweb.oxygen.api.service.permission.IRRoleGroupService +import top.fatweb.oxygen.api.service.permission.IUserService +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.util.RedisUtil +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.GroupWithRoleVo +import top.fatweb.oxygen.api.vo.permission.base.GroupVo + +/** + * Group service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RedisUtil + * @see IRRoleGroupService + * @see IUserService + * @see ServiceImpl + * @see GroupMapper + * @see Group + * @see IGroupService + */ +@Service +class GroupServiceImpl( + private val redisUtil: RedisUtil, + private val rRoleGroupService: IRRoleGroupService, + private val userService: IUserService +) : ServiceImpl(), IGroupService { + override fun getPage(groupGetParam: GroupGetParam?): PageVo { + val groupIdsPage = Page(groupGetParam?.currentPage ?: 1, groupGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(groupGetParam, groupIdsPage) + + val groupIdsIPage = + baseMapper.selectPage(groupIdsPage, groupGetParam?.searchName, groupGetParam?.searchRegex ?: false) + val groupPage = Page(groupIdsPage.current, groupIdsIPage.size, groupIdsIPage.total) + if (groupIdsIPage.total > 0) { + groupPage.setRecords(baseMapper.selectListWithRoleByIds(groupIdsIPage.records)) + } + + return GroupConverter.groupPageToGroupWithRolePageVo(groupPage) + } + + override fun getOne(id: Long): GroupWithRoleVo = + baseMapper.selectOneById(id)?.let(GroupConverter::groupToGroupWithRoleVo) ?: throw NoRecordFoundException() + + override fun getList(): List = this.list().map(GroupConverter::groupToGroupVo) + + @Transactional + override fun add(groupAddParam: GroupAddParam): GroupVo { + val group = GroupConverter.groupAddParamToGroup(groupAddParam) + if (baseMapper.insert(group) != 1) { + throw DatabaseInsertException() + } + + if (group.roles.isNullOrEmpty()) { + return GroupConverter.groupToGroupVo(group) + } + + rRoleGroupService.saveBatch(group.roles!!.map { + RRoleGroup().apply { + groupId = group.id + roleId = it.id + } + }) + + return GroupConverter.groupToGroupVo(group) + } + + @Transactional + override fun update(groupUpdateParam: GroupUpdateParam): GroupVo { + val group = GroupConverter.groupUpdateParamToGroup(groupUpdateParam) + + if (baseMapper.updateById(group) != 1) { + throw DatabaseUpdateException() + } + + val oldRoleList = rRoleGroupService.list( + KtQueryWrapper(RRoleGroup()).select(RRoleGroup::roleId).eq(RRoleGroup::groupId, groupUpdateParam.id) + ).map { it.roleId } + val addRoleIds = HashSet() + val removeRoleIds = HashSet() + groupUpdateParam.roleIds?.forEach(addRoleIds::add) + oldRoleList.forEach { + it?.let(removeRoleIds::add) + } + removeRoleIds.removeAll(addRoleIds) + oldRoleList.toSet().let(addRoleIds::removeAll) + + removeRoleIds.forEach { + rRoleGroupService.remove( + KtQueryWrapper(RRoleGroup()).eq( + RRoleGroup::groupId, groupUpdateParam.id + ).eq(RRoleGroup::roleId, it) + ) + } + + addRoleIds.forEach { + rRoleGroupService.save(RRoleGroup().apply { + groupId = groupUpdateParam.id + roleId = it + }) + } + + groupUpdateParam.id?.let { offlineUser(it) } + + return GroupConverter.groupToGroupVo(group) + } + + override fun status(groupUpdateStatusParam: GroupUpdateStatusParam) { + updateById(GroupConverter.groupUpdateStatusParamToGroup(groupUpdateStatusParam)).let { + if (!it) { + throw DatabaseUpdateException() + } + + if (!groupUpdateStatusParam.enable) { + groupUpdateStatusParam.id?.let { id -> offlineUser(id) } + } + } + } + + @Transactional + override fun deleteOne(id: Long) { + this.delete(GroupDeleteParam(listOf(id))) + } + + @Transactional + override fun delete(groupDeleteParam: GroupDeleteParam) { + baseMapper.deleteBatchIds(groupDeleteParam.ids) + rRoleGroupService.remove(KtQueryWrapper(RRoleGroup()).`in`(RRoleGroup::groupId, groupDeleteParam.ids)) + offlineUser(*groupDeleteParam.ids!!.toLongArray()) + } + + private fun offlineUser(vararg groupIds: Long) { + val userIds = userService.getIdsByGroupIds(groupIds.toList()) + WebUtil.offlineUser(redisUtil, *userIds.toLongArray()) + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/MenuServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/MenuServiceImpl.kt new file mode 100644 index 0000000..3c85e99 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/MenuServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.Menu +import top.fatweb.oxygen.api.mapper.permission.MenuMapper +import top.fatweb.oxygen.api.service.permission.IMenuService + +/** + * Menu service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see MenuMapper + * @see Menu + * @see IMenuService + */ +@Service +class MenuServiceImpl : ServiceImpl(), IMenuService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/ModuleServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/ModuleServiceImpl.kt new file mode 100644 index 0000000..23f0886 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/ModuleServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.Module +import top.fatweb.oxygen.api.mapper.permission.ModuleMapper +import top.fatweb.oxygen.api.service.permission.IModuleService + +/** + * Module service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see ModuleMapper + * @see Module + * @see IModuleService + */ +@Service +class ModuleServiceImpl : ServiceImpl(), IModuleService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/OperationServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/OperationServiceImpl.kt new file mode 100644 index 0000000..241efa9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/OperationServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.Operation +import top.fatweb.oxygen.api.mapper.permission.OperationMapper +import top.fatweb.oxygen.api.service.permission.IOperationService + +/** + * Operation service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see OperationMapper + * @see Operation + * @see IOperationService + */ +@Service +class OperationServiceImpl : ServiceImpl(), IOperationService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/PowerServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/PowerServiceImpl.kt new file mode 100644 index 0000000..d709f72 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/PowerServiceImpl.kt @@ -0,0 +1,38 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.converter.permission.PowerConverter +import top.fatweb.oxygen.api.entity.permission.Power +import top.fatweb.oxygen.api.entity.permission.PowerSet +import top.fatweb.oxygen.api.mapper.permission.PowerMapper +import top.fatweb.oxygen.api.service.permission.* + +/** + * Power service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IModuleService + * @see IMenuService + * @see IFuncService + * @see IOperationService + * @see ServiceImpl + * @see PowerMapper + * @see Power + * @see IPowerService + */ +@Service +class PowerServiceImpl( + private val moduleService: IModuleService, + private val menuService: IMenuService, + private val funcService: IFuncService, + private val operationService: IOperationService +) : ServiceImpl(), IPowerService { + override fun getList() = PowerConverter.powerSetToPowerSetVo(PowerSet().apply { + moduleList = moduleService.list() + menuList = menuService.list() + funcList = funcService.list() + operationList = operationService.list() + }) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RPowerRoleServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RPowerRoleServiceImpl.kt new file mode 100644 index 0000000..9522f91 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RPowerRoleServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.RPowerRole +import top.fatweb.oxygen.api.mapper.permission.RPowerRoleMapper +import top.fatweb.oxygen.api.service.permission.IRPowerRoleService + +/** + * Power role intermediate service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see RPowerRoleMapper + * @see RPowerRole + * @see IRPowerRoleService + */ +@Service +class RPowerRoleServiceImpl : ServiceImpl(), IRPowerRoleService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RRoleGroupServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RRoleGroupServiceImpl.kt new file mode 100644 index 0000000..e83e8b1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RRoleGroupServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.RRoleGroup +import top.fatweb.oxygen.api.mapper.permission.RRoleGroupMapper +import top.fatweb.oxygen.api.service.permission.IRRoleGroupService + +/** + * Role group intermediate service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see RRoleGroupMapper + * @see RRoleGroup + * @see IRRoleGroupService + */ +@Service +class RRoleGroupServiceImpl : ServiceImpl(), IRRoleGroupService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserGroupServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserGroupServiceImpl.kt new file mode 100644 index 0000000..12b5100 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserGroupServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.RUserGroup +import top.fatweb.oxygen.api.mapper.permission.RUserGroupMapper +import top.fatweb.oxygen.api.service.permission.IRUserGroupService + +/** + * User group intermediate service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see RUserGroupMapper + * @see RUserGroup + * @see IRUserGroupService + */ +@Service +class RUserGroupServiceImpl : ServiceImpl(), IRUserGroupService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserRoleServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserRoleServiceImpl.kt new file mode 100644 index 0000000..3a9d0b2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RUserRoleServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.RUserRole +import top.fatweb.oxygen.api.mapper.permission.RUserRoleMapper +import top.fatweb.oxygen.api.service.permission.IRUserRoleService + +/** + * User role intermediate service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see RUserRoleMapper + * @see RUserRole + * @see IRUserRoleService + */ +@Service +class RUserRoleServiceImpl : ServiceImpl(), IRUserRoleService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RoleServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RoleServiceImpl.kt new file mode 100644 index 0000000..dc40da1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/RoleServiceImpl.kt @@ -0,0 +1,187 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.permission.RoleConverter +import top.fatweb.oxygen.api.entity.permission.RPowerRole +import top.fatweb.oxygen.api.entity.permission.Role +import top.fatweb.oxygen.api.exception.DatabaseInsertException +import top.fatweb.oxygen.api.exception.DatabaseUpdateException +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.mapper.permission.RoleMapper +import top.fatweb.oxygen.api.param.permission.role.* +import top.fatweb.oxygen.api.service.permission.* +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.util.RedisUtil +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.RoleWithPowerVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo + +/** + * Role service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RedisUtil + * @see IRPowerRoleService + * @see IFuncService + * @see IMenuService + * @see IUserService + * @see ServiceImpl + * @see RoleMapper + * @see Role + * @see IRoleService + */ +@Service +class RoleServiceImpl( + private val redisUtil: RedisUtil, + private val rPowerRoleService: IRPowerRoleService, + private val funcService: IFuncService, + private val menuService: IMenuService, + private val userService: IUserService +) : ServiceImpl(), IRoleService { + override fun getPage(roleGetParam: RoleGetParam?): PageVo { + val roleIdsPage = Page(roleGetParam?.currentPage ?: 1, roleGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(roleGetParam, roleIdsPage) + + val roleIdsIPage = + baseMapper.selectPage(roleIdsPage, roleGetParam?.searchName, roleGetParam?.searchRegex ?: false) + + val rolePage = Page(roleIdsPage.current, roleIdsIPage.size, roleIdsIPage.total) + if (roleIdsIPage.total > 0) { + rolePage.setRecords(baseMapper.selectListWithPowerByIds(roleIdsIPage.records)) + } + + + return RoleConverter.rolePageToRoleWithPowerPageVo(rolePage) + } + + override fun getOne(id: Long): RoleWithPowerVo = + baseMapper.selectOneById(id)?.let(RoleConverter::roleToRoleWithPowerVo) ?: throw NoRecordFoundException() + + override fun getList(): List = this.list().map(RoleConverter::roleToRoleVo) + + @Transactional + override fun add(roleAddParam: RoleAddParam): RoleVo { + val fullPowerIds = roleAddParam.powerIds?.let(::getFullPowerIds) + + val role = RoleConverter.roleAddParamToRole(roleAddParam) + if (baseMapper.insert(role) != 1) { + throw DatabaseInsertException() + } + + if (fullPowerIds.isNullOrEmpty()) { + return RoleConverter.roleToRoleVo(role) + } + + rPowerRoleService.saveBatch(fullPowerIds.map { + RPowerRole().apply { + roleId = role.id + powerId = it + } + }) + return RoleConverter.roleToRoleVo(role) + } + + @Transactional + override fun update(roleUpdateParam: RoleUpdateParam): RoleVo { + val fullPowerIds = roleUpdateParam.powerIds?.let(::getFullPowerIds) + + val role = RoleConverter.roleUpdateParamToRole(roleUpdateParam) + + if (baseMapper.updateById(role) != 1) { + throw DatabaseUpdateException() + } + + val oldPowerList = rPowerRoleService.list( + KtQueryWrapper(RPowerRole()).select(RPowerRole::powerId).eq(RPowerRole::roleId, roleUpdateParam.id) + ).map { it.powerId } + val addPowerIds = HashSet() + val removePowerIds = HashSet() + fullPowerIds?.forEach(addPowerIds::add) + oldPowerList.forEach { + it?.let(removePowerIds::add) + } + removePowerIds.removeAll(addPowerIds) + oldPowerList.toSet().let(addPowerIds::removeAll) + + removePowerIds.forEach { + rPowerRoleService.remove( + KtQueryWrapper(RPowerRole()).eq( + RPowerRole::roleId, roleUpdateParam.id + ).eq(RPowerRole::powerId, it) + ) + } + + addPowerIds.forEach { + rPowerRoleService.save(RPowerRole().apply { + roleId = roleUpdateParam.id + powerId = it + }) + } + + roleUpdateParam.id?.let { offlineUser(it) } + + return RoleConverter.roleToRoleVo(role) + } + + override fun status(roleUpdateStatusParam: RoleUpdateStatusParam) { + updateById(RoleConverter.roleUpdateStatusParamToRole(roleUpdateStatusParam)).let { + if (!it) { + throw DatabaseUpdateException() + } + + if (!roleUpdateStatusParam.enable) { + roleUpdateStatusParam.id?.let { id -> offlineUser(id) } + } + } + } + + @Transactional + override fun deleteOne(id: Long) { + this.delete(RoleDeleteParam(listOf(id))) + } + + @Transactional + override fun delete(roleDeleteParam: RoleDeleteParam) { + baseMapper.deleteBatchIds(roleDeleteParam.ids) + rPowerRoleService.remove(KtQueryWrapper(RPowerRole()).`in`(RPowerRole::roleId, roleDeleteParam.ids)) + offlineUser(*roleDeleteParam.ids!!.toLongArray()) + } + + private fun getFullPowerIds(powerIds: List): Set { + val fullPowerIds: HashSet = hashSetOf() + powerIds.forEach { + fullPowerIds.add(it) + getElementParent(it / 100 * 100, fullPowerIds) + getMenuParent(it / 10000 * 10000, fullPowerIds) + fullPowerIds.add(it / 1000000 * 1000000) + } + + return fullPowerIds + } + + private fun getElementParent(id: Long, parentIds: HashSet) { + parentIds.add(id) + funcService.getById(id)?.parentId?.let { + getElementParent(it, parentIds) + } + } + + private fun getMenuParent(id: Long, parentIds: HashSet) { + parentIds.add(id) + menuService.getById(id)?.parentId?.let { + getMenuParent(it, parentIds) + } + } + + private fun offlineUser(vararg roleIds: Long) { + val userIds = userService.getIdsByRoleIds(roleIds.toList()) + WebUtil.offlineUser(redisUtil, *userIds.toLongArray()) + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserDetailsServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserDetailsServiceImpl.kt new file mode 100644 index 0000000..ad82c48 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserDetailsServiceImpl.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.core.userdetails.UserDetailsService +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.LoginUser +import top.fatweb.oxygen.api.exception.UserNotFoundException +import top.fatweb.oxygen.api.service.permission.IUserService + +/** + * User details service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IUserService + * @see UserDetailsService + */ +@Service +class UserDetailsServiceImpl( + private val userService: IUserService +) : UserDetailsService { + override fun loadUserByUsername(account: String): UserDetails { + val user = userService.getUserWithPowerByAccount(account) + user ?: throw UserNotFoundException() + + return LoginUser(user) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserInfoServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserInfoServiceImpl.kt new file mode 100644 index 0000000..2e92d4b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserInfoServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.mapper.permission.UserInfoMapper +import top.fatweb.oxygen.api.service.permission.IUserInfoService + +/** + * User information service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see UserInfoMapper + * @see UserInfo + * @see IUserInfoService + */ +@Service +class UserInfoServiceImpl : ServiceImpl(), IUserInfoService diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserServiceImpl.kt new file mode 100644 index 0000000..059b22c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/permission/impl/UserServiceImpl.kt @@ -0,0 +1,329 @@ +package top.fatweb.oxygen.api.service.permission.impl + +import com.baomidou.dynamic.datasource.annotation.DS +import com.baomidou.mybatisplus.core.metadata.OrderItem +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.security.access.AccessDeniedException +import org.springframework.security.authentication.BadCredentialsException +import org.springframework.security.crypto.password.PasswordEncoder +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.permission.UserConverter +import top.fatweb.oxygen.api.entity.permission.RUserGroup +import top.fatweb.oxygen.api.entity.permission.RUserRole +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.entity.permission.UserInfo +import top.fatweb.oxygen.api.exception.DatabaseInsertException +import top.fatweb.oxygen.api.exception.DatabaseUpdateException +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.exception.UserNotFoundException +import top.fatweb.oxygen.api.mapper.permission.UserMapper +import top.fatweb.oxygen.api.param.permission.user.* +import top.fatweb.oxygen.api.service.permission.* +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.util.RedisUtil +import top.fatweb.oxygen.api.util.StrUtil +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPasswordRoleInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithPowerInfoVo +import top.fatweb.oxygen.api.vo.permission.UserWithRoleInfoVo +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.util.* + +/** + * User service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PasswordEncoder + * @see RedisUtil + * @see IUserInfoService + * @see IModuleService + * @see IMenuService + * @see IFuncService + * @see IOperationService + * @see IRUserRoleService + * @see IRUserGroupService + * @see ServiceImpl + * @see UserMapper + * @see User + * @see IUserService + */ +@DS("master") +@Service +class UserServiceImpl( + private val passwordEncoder: PasswordEncoder, + private val redisUtil: RedisUtil, + private val userInfoService: IUserInfoService, + private val moduleService: IModuleService, + private val menuService: IMenuService, + private val funcService: IFuncService, + private val operationService: IOperationService, + private val rUserRoleService: IRUserRoleService, + private val rUserGroupService: IRUserGroupService +) : ServiceImpl(), IUserService { + override fun getUserWithPowerByAccount(account: String): User? { + val user = baseMapper.selectOneWithPowerInfoByAccount(account) + user ?: return null + + if (user.id == 0L) { + user.modules = moduleService.list() + user.menus = menuService.list() + user.funcs = funcService.list() + user.operations = operationService.list() + } + + return user + } + + override fun getInfo(): UserWithPowerInfoVo = + WebUtil.getLoginUsername()?.let(::getUserWithPowerByAccount)?.let(UserConverter::userToUserWithPowerInfoVo) + ?: throw UserNotFoundException() + + override fun getBasicInfo(username: String): UserWithInfoVo = + baseMapper.selectOneWithBasicInfoByUsername(username)?.let(UserConverter::userToUserWithInfoVo) + ?: throw NoRecordFoundException() + + + override fun updateInfo(userInfoUpdateParam: UserInfoUpdateParam): Boolean { + val userId = WebUtil.getLoginUserId() ?: throw AccessDeniedException("Access denied") + return userInfoService.update( + KtUpdateWrapper(UserInfo()).eq(UserInfo::userId, userId) + .set(UserInfo::avatar, userInfoUpdateParam.avatar) + .set(UserInfo::nickname, userInfoUpdateParam.nickname) + ) + } + + override fun password(userChangePasswordParam: UserChangePasswordParam) { + val user = this.getById(WebUtil.getLoginUserId() ?: throw AccessDeniedException("Access denied")) + user?.let { + if (!passwordEncoder.matches(userChangePasswordParam.originalPassword, user.password)) { + throw BadCredentialsException("Passwords do not match") + } + val wrapper = KtUpdateWrapper(User()) + .eq(User::id, user.id) + .set(User::password, passwordEncoder.encode(userChangePasswordParam.newPassword)) + .set(User::credentialsExpiration, null) + .set(User::updateTime, LocalDateTime.now(ZoneOffset.UTC)) + + if (!this.update(wrapper)) { + throw DatabaseUpdateException() + } + + WebUtil.offlineUser(redisUtil, user.id!!) + } ?: throw NoRecordFoundException() + } + + override fun getOne(id: Long): UserWithRoleInfoVo = + baseMapper.selectOneWithRoleInfoById(id)?.let(UserConverter::userToUserWithRoleInfoVo) + ?: throw UserNotFoundException() + + override fun getPage(userGetParam: UserGetParam?): PageVo { + val userIdsPage = Page(userGetParam?.currentPage ?: 1, userGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(userGetParam, userIdsPage, OrderItem.asc("id")) + + val userIdsIPage = + baseMapper.selectPage( + userIdsPage, + userGetParam?.searchType ?: "ALL", + userGetParam?.searchValue, + userGetParam?.searchRegex ?: false + ) + val userPage = Page(userIdsIPage.current, userIdsIPage.size, userIdsIPage.total) + if (userIdsIPage.total > 0) { + userPage.setRecords(baseMapper.selectListWithRoleInfoByIds(userIdsIPage.records)) + } + + return UserConverter.userPageToUserWithRoleInfoPageVo(userPage) + } + + override fun getList() = baseMapper.selectListWithInfo().map(UserConverter::userToUserWithInfoVo) + + @Transactional + override fun add(userAddParam: UserAddParam): UserWithPasswordRoleInfoVo { + val rawPassword = + if (userAddParam.password.isNullOrBlank()) StrUtil.getRandomPassword(10) else userAddParam.password + val user = UserConverter.userAddParamToUser(userAddParam) + + user.apply { + password = passwordEncoder.encode(rawPassword) + verify = if (userAddParam.verified) null else "${ + LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli() + }-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}" + } + + if (!this.save(user)) { + throw DatabaseInsertException() + } + + + user.userInfo?.apply { userId = user.id }?.let(userInfoService::save) + + if (!user.roles.isNullOrEmpty()) { + rUserRoleService.saveBatch(user.roles!!.map { + RUserRole().apply { + userId = user.id + roleId = it.id + } + }) + } + + if (!user.groups.isNullOrEmpty()) { + rUserGroupService.saveBatch(user.groups!!.map { + RUserGroup().apply { + userId = user.id + groupId = it.id + } + }) + } + + user.password = rawPassword + + return UserConverter.userToUserWithPasswordRoleInfoVo(user) + } + + @Transactional + override fun update(userUpdateParam: UserUpdateParam): UserWithRoleInfoVo { + val user = UserConverter.userUpdateParamToUser(userUpdateParam) + user.updateTime = LocalDateTime.now(ZoneOffset.UTC) + + val oldRoleList = rUserRoleService.list( + KtQueryWrapper(RUserRole()).select(RUserRole::roleId).eq(RUserRole::userId, userUpdateParam.id) + ).map { it.roleId } + val addRoleIds = HashSet() + val removeRoleIds = HashSet() + userUpdateParam.roleIds?.forEach(addRoleIds::add) + oldRoleList.forEach { + it?.let(removeRoleIds::add) + } + removeRoleIds.removeAll(addRoleIds) + oldRoleList.toSet().let(addRoleIds::removeAll) + + val oldGroupList = rUserGroupService.list( + KtQueryWrapper(RUserGroup()).select(RUserGroup::groupId).eq(RUserGroup::userId, userUpdateParam.id) + ).map { it.groupId } + val addGroupIds = HashSet() + val removeGroupIds = HashSet() + userUpdateParam.groupIds?.forEach(addGroupIds::add) + oldGroupList.forEach { + it?.let(removeGroupIds::add) + } + removeGroupIds.removeAll(addGroupIds) + oldGroupList.toSet().let(addGroupIds::removeAll) + + this.updateById(user) + this.update( + KtUpdateWrapper(User()).eq(User::id, user.id) + .set( + User::verify, + if (userUpdateParam.verified || userUpdateParam.id == 0L) null else "${ + LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli() + }-${UUID.randomUUID()}-${UUID.randomUUID()}-${UUID.randomUUID()}" + ) + .set(User::expiration, if (userUpdateParam.id == 0L) null else user.expiration) + .set(User::credentialsExpiration, if (userUpdateParam.id == 0L) null else user.credentialsExpiration) + ) + + user.userInfo?.let { userInfo -> + userInfoService.getOne( + KtQueryWrapper(UserInfo()).select(UserInfo::id).eq(UserInfo::userId, userUpdateParam.id) + )?.let { + userInfo.id = it.id + userInfoService.updateById(userInfo) + } + } + + removeRoleIds.forEach { + rUserRoleService.remove( + KtQueryWrapper(RUserRole()).eq( + RUserRole::userId, userUpdateParam.id + ).eq(RUserRole::roleId, it) + ) + } + + addRoleIds.forEach { + rUserRoleService.save(RUserRole().apply { + userId = userUpdateParam.id + roleId = it + }) + } + + removeGroupIds.forEach { + rUserGroupService.remove( + KtQueryWrapper(RUserGroup()).eq( + RUserGroup::userId, userUpdateParam.id + ).eq(RUserGroup::groupId, it) + ) + } + + addGroupIds.forEach { + rUserGroupService.save(RUserGroup().apply { + userId = userUpdateParam.id + groupId = it + }) + } + + userUpdateParam.id?.let { WebUtil.offlineUser(redisUtil, it) } + + return UserConverter.userToUserWithRoleInfoVo(user) + } + + override fun password(userUpdatePasswordParam: UserUpdatePasswordParam) { + if (WebUtil.getLoginUserId() != 0L && userUpdatePasswordParam.id == 0L) { + throw AccessDeniedException("Access denied") + } + + val user = this.getById(userUpdatePasswordParam.id) + user?.let { + val wrapper = KtUpdateWrapper(User()) + .eq(User::id, user.id) + .set(User::password, passwordEncoder.encode(userUpdatePasswordParam.password)) + .set( + User::credentialsExpiration, + if (user.id != 0L) userUpdatePasswordParam.credentialsExpiration else null + ) + .set(User::updateTime, LocalDateTime.now(ZoneOffset.UTC)) + + if (!this.update(wrapper)) { + throw DatabaseUpdateException() + } + + userUpdatePasswordParam.id?.let { WebUtil.offlineUser(redisUtil, it) } + } ?: throw NoRecordFoundException() + } + + @Transactional + override fun deleteOne(id: Long) { + if (id == 0L) { + return + } + + this.delete(UserDeleteParam(listOf(id))) + } + + @Transactional + override fun delete(userDeleteParam: UserDeleteParam) { + val ids = userDeleteParam.ids!!.filter { it != 0L } + if (ids.isEmpty()) { + return + } + + this.removeBatchByIds(ids) + userInfoService.remove(KtQueryWrapper(UserInfo()).`in`(UserInfo::userId, ids)) + rUserRoleService.remove(KtQueryWrapper(RUserRole()).`in`(RUserRole::userId, ids)) + rUserGroupService.remove(KtQueryWrapper(RUserGroup()).`in`(RUserGroup::userId, ids)) + + WebUtil.offlineUser(redisUtil, *ids.toLongArray()) + } + + override fun getIdsByRoleIds(roleIds: List) = baseMapper.selectIdsWithRoleIds(roleIds) + + override fun getIdsByGroupIds(groupIds: List) = baseMapper.selectIdsWithGroupIds(groupIds) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/IEventLogService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IEventLogService.kt new file mode 100644 index 0000000..ca2693c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IEventLogService.kt @@ -0,0 +1,22 @@ +package top.fatweb.oxygen.api.service.system + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.annotation.EventLogRecord +import top.fatweb.oxygen.api.entity.system.EventLog + +/** + * Event log service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface IEventLogService : IService { + /** + * Save event + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see EventLogRecord + */ + fun saveEvent(annotation: EventLogRecord, userId: Long) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISensitiveWordService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISensitiveWordService.kt new file mode 100644 index 0000000..c51606e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISensitiveWordService.kt @@ -0,0 +1,58 @@ +package top.fatweb.oxygen.api.service.system + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.system.SensitiveWord +import top.fatweb.oxygen.api.param.system.SensitiveWordAddParam +import top.fatweb.oxygen.api.param.system.SensitiveWordUpdateParam +import top.fatweb.oxygen.api.vo.system.SensitiveWordVo + +/** + * Sensitive word service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface ISensitiveWordService : IService { + /** + * Get sensitive word settings + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordVo + */ + fun get(): List + + /** + * Add sensitive word settings item + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordAddParam + */ + fun add(sensitiveWordAddParam: SensitiveWordAddParam) + + /** + * Update sensitive word settings + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWordUpdateParam + */ + fun update(sensitiveWordUpdateParam: SensitiveWordUpdateParam) + + /** + * Delete sensitive word settings item + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long) + + /** + * Check sensitive word + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun checkSensitiveWord(str: String) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISettingsService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISettingsService.kt new file mode 100644 index 0000000..00e8e28 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISettingsService.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.service.system + +import top.fatweb.oxygen.api.param.system.BaseSettingsParam +import top.fatweb.oxygen.api.param.system.MailSendParam +import top.fatweb.oxygen.api.param.system.MailSettingsParam +import top.fatweb.oxygen.api.param.system.TwoFactorSettingsParam +import top.fatweb.oxygen.api.vo.system.BaseSettingsVo +import top.fatweb.oxygen.api.vo.system.MailSettingsVo +import top.fatweb.oxygen.api.vo.system.TwoFactorSettingsVo + +/** + * Settings service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface ISettingsService { + /** + * Get base settings + * + * @return BaseSettingsVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseSettingsVo + */ + fun getBase(): BaseSettingsVo? + + /** + * Update base settings + * + * @param baseSettingsParam Base settings parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BaseSettingsParam + */ + fun updateBase(baseSettingsParam: BaseSettingsParam) + + /** + * Get mail settings + * + * @return MailSettingsVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSettingsVo + */ + fun getMail(): MailSettingsVo? + + /** + * Update mail settings + * + * @param mailSettingsParam Mail settings parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSettingsParam + */ + fun updateMail(mailSettingsParam: MailSettingsParam) + + /** + * Send mail + * + * @param mailSendParam Send mail parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSettingsParam + */ + fun sendMail(mailSendParam: MailSendParam) + + /** + * Get two-factor settings + * + * @return TwoFactorSettingsVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorSettingsVo + */ + fun getTwoFactor(): TwoFactorSettingsVo? + + /** + * Update two-factor settings + * + * @param twoFactorSettingsParam Two-factor settings parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TwoFactorSettingsParam + */ + fun updateTwoFactor(twoFactorSettingsParam: TwoFactorSettingsParam) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsLogService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsLogService.kt new file mode 100644 index 0000000..571149a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsLogService.kt @@ -0,0 +1,12 @@ +package top.fatweb.oxygen.api.service.system + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.system.StatisticsLog + +/** + * Statistics log service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface IStatisticsLogService : IService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsService.kt new file mode 100644 index 0000000..c698c8d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/IStatisticsService.kt @@ -0,0 +1,69 @@ +package top.fatweb.oxygen.api.service.system + +import top.fatweb.oxygen.api.param.system.ActiveInfoGetParam +import top.fatweb.oxygen.api.param.system.OnlineInfoGetParam +import top.fatweb.oxygen.api.vo.system.* + +/** + * Statistics service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +interface IStatisticsService { + /** + * Get software information + * + * @return SoftwareInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SoftwareInfoVo + */ + fun software(): SoftwareInfoVo + + /** + * Get hardware information + * + * @return HardwareInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HardwareInfoVo + */ + fun hardware(): HardwareInfoVo + + /** + * Get CPU information + * + * @return CpuInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see CpuInfoVo + */ + fun cpu(): CpuInfoVo + + /** + * Get storage information + * + * @return StorageInfoVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see StorageInfoVo + */ + fun storage(): StorageInfoVo + + /** + * Get the history of online users information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun online(onlineInfoGetParam: OnlineInfoGetParam?): OnlineInfoVo + + /** + * Get the history of active information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun active(activeInfoGetParam: ActiveInfoGetParam?): ActiveInfoVo +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISysLogService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISysLogService.kt new file mode 100644 index 0000000..16430ca --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/ISysLogService.kt @@ -0,0 +1,30 @@ +package top.fatweb.oxygen.api.service.system + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.system.SysLog +import top.fatweb.oxygen.api.param.system.SysLogGetParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.system.SysLogVo + +/** + * System log service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see SysLog + */ +interface ISysLogService : IService { + /** + * Get system log in page + * + * @param sysLogGetParam Get system log parameters + * @return IPage object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SysLogGetParam + * @see PageVo + * @see SysLogVo + */ + fun getPage(sysLogGetParam: SysLogGetParam?): PageVo +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/EventLogServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/EventLogServiceImpl.kt new file mode 100644 index 0000000..37ff2df --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/EventLogServiceImpl.kt @@ -0,0 +1,41 @@ +package top.fatweb.oxygen.api.service.system.impl + +import com.baomidou.dynamic.datasource.annotation.DS +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.annotation.EventLogRecord +import top.fatweb.oxygen.api.entity.system.EventLog +import top.fatweb.oxygen.api.mapper.system.EventLogMapper +import top.fatweb.oxygen.api.service.system.IEventLogService + +/** + * Event log service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see EventLogMapper + * @see EventLog + * @see IEventLogService + */ +@DS("sqlite") +@Service +class EventLogServiceImpl : ServiceImpl(), IEventLogService { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun saveEvent(annotation: EventLogRecord, userId: Long) { + try { + this.save(EventLog().apply { + this.event = annotation.event + operateUserId = userId + }) + } catch (e: Exception) { + logger.error("Cannot record event!!!", e) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SensitiveWordServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SensitiveWordServiceImpl.kt new file mode 100644 index 0000000..f60c2f0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SensitiveWordServiceImpl.kt @@ -0,0 +1,68 @@ +package top.fatweb.oxygen.api.service.system.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Propagation +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.system.SettingsConverter +import top.fatweb.oxygen.api.entity.system.SensitiveWord +import top.fatweb.oxygen.api.exception.MatchSensitiveWordException +import top.fatweb.oxygen.api.mapper.system.SensitiveWordMapper +import top.fatweb.oxygen.api.param.system.SensitiveWordAddParam +import top.fatweb.oxygen.api.param.system.SensitiveWordUpdateParam +import top.fatweb.oxygen.api.service.system.ISensitiveWordService +import top.fatweb.oxygen.api.vo.system.SensitiveWordVo + +/** + * Sensitive word service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see SensitiveWordMapper + * @see SensitiveWord + * @see ISensitiveWordService + */ +@Service +class SensitiveWordServiceImpl : ServiceImpl(), ISensitiveWordService { + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun get(): List = this.list().map(SettingsConverter::sensitiveWordToSensitiveWordVo) + + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun add(sensitiveWordAddParam: SensitiveWordAddParam) { + checkSensitiveWord(sensitiveWordAddParam.word!!) + this.save(SettingsConverter.sensitiveWordAddParamToSensitiveWord(sensitiveWordAddParam)) + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun update(sensitiveWordUpdateParam: SensitiveWordUpdateParam) { + this.update(KtUpdateWrapper(SensitiveWord()).set(SensitiveWord::enable, false)) + this.update( + KtUpdateWrapper(SensitiveWord()).`in`(SensitiveWord::id, sensitiveWordUpdateParam.ids) + .set(SensitiveWord::enable, true) + ) + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun delete(id: Long) { + this.removeById(id) + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + override fun checkSensitiveWord(str: String) { + this.list(KtQueryWrapper(SensitiveWord()).eq(SensitiveWord::enable, 1)).map(SensitiveWord::word).forEach { + it ?: return@forEach + + try { + if (Regex(it, RegexOption.IGNORE_CASE).matches(str)) { + throw MatchSensitiveWordException() + } + } catch (e: MatchSensitiveWordException) { + throw e + } catch (_: Exception) { + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SettingsServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SettingsServiceImpl.kt new file mode 100644 index 0000000..46b02d0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SettingsServiceImpl.kt @@ -0,0 +1,97 @@ +package top.fatweb.oxygen.api.service.system.impl + +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.param.system.BaseSettingsParam +import top.fatweb.oxygen.api.param.system.MailSendParam +import top.fatweb.oxygen.api.param.system.MailSettingsParam +import top.fatweb.oxygen.api.param.system.TwoFactorSettingsParam +import top.fatweb.oxygen.api.properties.ServerProperties +import top.fatweb.oxygen.api.service.system.ISettingsService +import top.fatweb.oxygen.api.settings.BaseSettings +import top.fatweb.oxygen.api.settings.MailSettings +import top.fatweb.oxygen.api.settings.SettingsOperator +import top.fatweb.oxygen.api.settings.TwoFactorSettings +import top.fatweb.oxygen.api.util.MailUtil +import top.fatweb.oxygen.api.util.StrUtil +import top.fatweb.oxygen.api.vo.system.BaseSettingsVo +import top.fatweb.oxygen.api.vo.system.MailSettingsVo +import top.fatweb.oxygen.api.vo.system.TwoFactorSettingsVo + +/** + * Settings service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ISettingsService + */ +@Service +class SettingsServiceImpl : ISettingsService { + override fun getBase() = BaseSettingsVo( + appName = SettingsOperator.getAppValue(BaseSettings::appName, "氧工具"), + appUrl = SettingsOperator.getAppValue(BaseSettings::appUrl, "http://localhost"), + verifyUrl = SettingsOperator.getAppValue( + BaseSettings::verifyUrl, + "http://localhost/verify?code=\${verifyCode}" + ), + retrieveUrl = SettingsOperator.getAppValue( + BaseSettings::retrieveUrl, + "http://localhost/forget?code=\${retrieveCode}" + ) + ) + + override fun updateBase(baseSettingsParam: BaseSettingsParam) { + baseSettingsParam.run { + SettingsOperator.setAppValue(BaseSettings::appName, appName) + SettingsOperator.setAppValue(BaseSettings::appUrl, appUrl) + SettingsOperator.setAppValue(BaseSettings::verifyUrl, verifyUrl) + SettingsOperator.setAppValue(BaseSettings::retrieveUrl, retrieveUrl) + } + } + + override fun getMail() = MailSettingsVo( + host = SettingsOperator.getMailValue(MailSettings::host), + port = SettingsOperator.getMailValue(MailSettings::port), + securityType = SettingsOperator.getMailValue(MailSettings::securityType), + username = SettingsOperator.getMailValue(MailSettings::username), + password = SettingsOperator.getMailValue(MailSettings::password)?.let(StrUtil::md5), + from = SettingsOperator.getMailValue(MailSettings::from), + fromName = SettingsOperator.getMailValue(MailSettings::fromName) + ) + + override fun updateMail(mailSettingsParam: MailSettingsParam) { + mailSettingsParam.run { + SettingsOperator.setMailValue(MailSettings::host, host) + SettingsOperator.setMailValue(MailSettings::port, port) + SettingsOperator.setMailValue(MailSettings::securityType, securityType) + SettingsOperator.setMailValue(MailSettings::username, username) + SettingsOperator.setMailValue(MailSettings::password, password) + SettingsOperator.setMailValue(MailSettings::from, from) + SettingsOperator.setMailValue(MailSettings::fromName, fromName) + } + + MailUtil.init() + } + + override fun sendMail(mailSendParam: MailSendParam) { + mailSendParam.to?.let { + MailUtil.sendSimpleMail( + "${ServerProperties.appName} Test Message", + "This is a test email sent when testing the system email sending service.", + false, + it + ) + } + } + + override fun getTwoFactor()= TwoFactorSettingsVo( + issuer = SettingsOperator.getTwoFactorValue(TwoFactorSettings::issuer, "OxygenToolbox"), + secretKeyLength = SettingsOperator.getTwoFactorValue(TwoFactorSettings::secretKeyLength, 16) + ) + + override fun updateTwoFactor(twoFactorSettingsParam: TwoFactorSettingsParam) { + twoFactorSettingsParam.run { + SettingsOperator.setTwoFactorValue(TwoFactorSettings::issuer, issuer) + SettingsOperator.setTwoFactorValue(TwoFactorSettings::secretKeyLength, secretKeyLength) + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsLogServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsLogServiceImpl.kt new file mode 100644 index 0000000..1c3f1c8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsLogServiceImpl.kt @@ -0,0 +1,22 @@ +package top.fatweb.oxygen.api.service.system.impl + +import com.baomidou.dynamic.datasource.annotation.DS +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.system.StatisticsLog +import top.fatweb.oxygen.api.mapper.system.StatisticsLogMapper +import top.fatweb.oxygen.api.service.system.IStatisticsLogService + +/** + * Statistics log service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see StatisticsLogMapper + * @see StatisticsLog + * @see IStatisticsLogService + */ +@DS("sqlite") +@Service +class StatisticsLogServiceImpl : ServiceImpl(), IStatisticsLogService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsServiceImpl.kt new file mode 100644 index 0000000..a5f936e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/StatisticsServiceImpl.kt @@ -0,0 +1,238 @@ +package top.fatweb.oxygen.api.service.system.impl + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import org.springframework.stereotype.Service +import oshi.SystemInfo +import oshi.hardware.CentralProcessor +import top.fatweb.oxygen.api.entity.system.EventLog +import top.fatweb.oxygen.api.entity.system.StatisticsLog +import top.fatweb.oxygen.api.param.system.ActiveInfoGetParam +import top.fatweb.oxygen.api.param.system.OnlineInfoGetParam +import top.fatweb.oxygen.api.properties.SecurityProperties +import top.fatweb.oxygen.api.properties.ServerProperties +import top.fatweb.oxygen.api.service.system.IEventLogService +import top.fatweb.oxygen.api.service.system.IStatisticsLogService +import top.fatweb.oxygen.api.service.system.IStatisticsService +import top.fatweb.oxygen.api.util.ByteUtil +import top.fatweb.oxygen.api.util.RedisUtil +import top.fatweb.oxygen.api.vo.system.* +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter +import java.util.* +import java.util.concurrent.TimeUnit + +/** + * Statistics service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RedisUtil + * @see IStatisticsLogService + * @see IEventLogService + * @see IStatisticsService + */ +@Service +class StatisticsServiceImpl( + private val redisUtil: RedisUtil, + private val statisticsLogService: IStatisticsLogService, + private val eventLogService: IEventLogService +) : IStatisticsService { + private val systemProperties: Properties = System.getProperties() + private val systemInfo: SystemInfo = SystemInfo() + private val runtime: Runtime = Runtime.getRuntime() + + override fun software() = SoftwareInfoVo( + os = systemInfo.operatingSystem.toString(), + bitness = systemInfo.operatingSystem.bitness, + javaVersion = systemProperties.getProperty("java.version"), + javaVersionDate = systemProperties.getProperty("java.version.date"), + javaVendor = systemProperties.getProperty("java.vendor"), + javaRuntime = systemProperties.getProperty("java.runtime.name"), + javaRuntimeVersion = systemProperties.getProperty("java.runtime.version"), + jvm = systemProperties.getProperty("java.vm.name"), + jvmVersion = systemProperties.getProperty("java.vm.version"), + jvmInfo = systemProperties.getProperty("java.vm.info"), + jvmVendor = systemProperties.getProperty("java.vm.vendor"), + javaClassVersion = systemProperties.getProperty("java.class.version"), + osBootTime = LocalDateTime.ofEpochSecond(systemInfo.operatingSystem.systemBootTime, 0, ZoneOffset.UTC), + serverStartupTime = ServerProperties.startupTime + ) + + override fun hardware() = HardwareInfoVo( + cpu = systemInfo.hardware.processor.processorIdentifier.name, + arch = systemProperties.getProperty("os.arch"), + is64Bit = systemInfo.hardware.processor.processorIdentifier.isCpu64bit, + cpuPhysicalPackageCount = systemInfo.hardware.processor.physicalPackageCount, + cpuPhysicalProcessorCount = systemInfo.hardware.processor.physicalProcessorCount, + cpuLogicalProcessorCount = systemInfo.hardware.processor.logicalProcessorCount, + microarchitecture = systemInfo.hardware.processor.processorIdentifier.microarchitecture, + memories = "${ByteUtil.formatByteSize(systemInfo.hardware.memory.total)} (${ + if (systemInfo.hardware.memory.physicalMemory.size > 0) systemInfo.hardware.memory.physicalMemory.joinToString( + " + " + ) { ByteUtil.formatByteSize(it.capacity) } + else "Unknown" + })", + disks = if (systemInfo.hardware.diskStores.size > 0) "${ByteUtil.formatByteSize(systemInfo.hardware.diskStores.sumOf { it.size })} (${ + systemInfo.hardware.diskStores.joinToString( + " + " + ) { ByteUtil.formatByteSize(it.size) } + })" + else "Unknown" + ) + + override fun cpu(): CpuInfoVo { + val processor = systemInfo.hardware.processor + val prevTicks = processor.systemCpuLoadTicks + val processorPrevTicksList = processor.processorCpuLoadTicks + TimeUnit.MILLISECONDS.sleep(500) + val ticks = processor.systemCpuLoadTicks + val processorTicksList = processor.processorCpuLoadTicks + + val user = ticks[CentralProcessor.TickType.USER.index] - prevTicks[CentralProcessor.TickType.USER.index] + val nice = ticks[CentralProcessor.TickType.NICE.index] - prevTicks[CentralProcessor.TickType.NICE.index] + val system = ticks[CentralProcessor.TickType.SYSTEM.index] - prevTicks[CentralProcessor.TickType.SYSTEM.index] + val idle = ticks[CentralProcessor.TickType.IDLE.index] - prevTicks[CentralProcessor.TickType.IDLE.index] + val iowait = ticks[CentralProcessor.TickType.IOWAIT.index] - prevTicks[CentralProcessor.TickType.IOWAIT.index] + val irq = ticks[CentralProcessor.TickType.IRQ.index] - prevTicks[CentralProcessor.TickType.IRQ.index] + val softirq = + ticks[CentralProcessor.TickType.SOFTIRQ.index] - prevTicks[CentralProcessor.TickType.SOFTIRQ.index] + val steal = ticks[CentralProcessor.TickType.STEAL.index] - prevTicks[CentralProcessor.TickType.STEAL.index] + val total = user + nice + system + idle + iowait + irq + softirq + steal + return CpuInfoVo( + user, + nice, + system, + idle, + iowait, + irq, + softirq, + steal, + total, + listOf(*processorPrevTicksList.mapIndexed { index, processorPrevTicks -> + val processorTicks = processorTicksList[index] + val processorUser = + processorTicks[CentralProcessor.TickType.USER.index] - processorPrevTicks[CentralProcessor.TickType.USER.index] + val processorNice = + processorTicks[CentralProcessor.TickType.NICE.index] - processorPrevTicks[CentralProcessor.TickType.NICE.index] + val processorSystem = + processorTicks[CentralProcessor.TickType.SYSTEM.index] - processorPrevTicks[CentralProcessor.TickType.SYSTEM.index] + val processorIdle = + processorTicks[CentralProcessor.TickType.IDLE.index] - processorPrevTicks[CentralProcessor.TickType.IDLE.index] + val processorIowait = + processorTicks[CentralProcessor.TickType.IOWAIT.index] - processorPrevTicks[CentralProcessor.TickType.IOWAIT.index] + val processorIrq = + processorTicks[CentralProcessor.TickType.IRQ.index] - processorPrevTicks[CentralProcessor.TickType.IRQ.index] + val processorSoftirq = + processorTicks[CentralProcessor.TickType.SOFTIRQ.index] - processorPrevTicks[CentralProcessor.TickType.SOFTIRQ.index] + val processorSteal = + processorTicks[CentralProcessor.TickType.STEAL.index] - processorPrevTicks[CentralProcessor.TickType.STEAL.index] + val processorTotal = + processorUser + processorNice + processorSystem + processorIdle + processorIowait + processorIrq + processorSoftirq + processorSteal + CpuInfoVo( + processorUser, + processorNice, + processorSystem, + processorIdle, + processorIowait, + processorIrq, + processorSoftirq, + processorSteal, + processorTotal + ) + + }.toTypedArray()) + ) + } + + override fun storage() = StorageInfoVo( + memoryTotal = systemInfo.hardware.memory.total, + memoryFree = systemInfo.hardware.memory.available, + virtualMemoryMax = systemInfo.hardware.memory.virtualMemory.virtualMax, + virtualMemoryInUse = systemInfo.hardware.memory.virtualMemory.virtualInUse, + swapTotal = systemInfo.hardware.memory.virtualMemory.swapTotal, + swapUsed = systemInfo.hardware.memory.virtualMemory.swapUsed, + jvmTotal = runtime.totalMemory(), + jvmFree = runtime.freeMemory(), + fileStores = systemInfo.operatingSystem.fileSystem.fileStores.map { + FileStoreInfoVo( + mount = it.mount, + total = it.totalSpace, + free = it.freeSpace + ) + } + ) + + override fun online(onlineInfoGetParam: OnlineInfoGetParam?): OnlineInfoVo { + val history: List = statisticsLogService.list( + KtQueryWrapper(StatisticsLog()) + .select(StatisticsLog::value, StatisticsLog::recordTime) + .eq(StatisticsLog::key, StatisticsLog.KeyItem.ONLINE_USERS_COUNT) + .between( + onlineInfoGetParam?.scope != OnlineInfoGetParam.Scope.ALL, + StatisticsLog::recordTime, + LocalDateTime.now(ZoneOffset.UTC).run { + when (onlineInfoGetParam?.scope) { + OnlineInfoGetParam.Scope.DAY -> minusDays(1) + OnlineInfoGetParam.Scope.MONTH -> minusMonths(1) + OnlineInfoGetParam.Scope.QUARTER -> minusMonths(3) + OnlineInfoGetParam.Scope.YEAR -> minusYears(1) + OnlineInfoGetParam.Scope.TWO_YEARS -> minusYears(2) + OnlineInfoGetParam.Scope.THREE_YEARS -> minusYears(3) + OnlineInfoGetParam.Scope.FIVE_YEARS -> minusYears(5) + else -> minusWeeks(1) + } + }.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + LocalDateTime.now(ZoneOffset.UTC).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + ) + ).map { + OnlineInfoVo.HistoryVo( + time = it.recordTime!!, + record = it.value!! + ) + } + + return OnlineInfoVo( + current = redisUtil.keys("${SecurityProperties.jwtIssuer}_login_*") + .distinctBy { + Regex("${SecurityProperties.jwtIssuer}_login_(.*):.*").matchEntire(it)?.groupValues?.getOrNull( + 1 + ) + }.size.toLong(), + history = history + ) + } + + override fun active(activeInfoGetParam: ActiveInfoGetParam?): ActiveInfoVo { + fun getHistory(event: String) = eventLogService.listMaps( + QueryWrapper().select("strftime('%Y-%m-%d', operate_time) time, count(distinct operate_user_id) count") + .eq("event", event).groupBy("time").between( + activeInfoGetParam?.scope != ActiveInfoGetParam.Scope.ALL, + "operate_time", + LocalDateTime.now(ZoneOffset.UTC).run { + when (activeInfoGetParam?.scope) { + ActiveInfoGetParam.Scope.MONTH -> minusMonths(1) + ActiveInfoGetParam.Scope.QUARTER -> minusMonths(3) + ActiveInfoGetParam.Scope.YEAR -> minusYears(1) + ActiveInfoGetParam.Scope.TWO_YEARS -> minusYears(2) + ActiveInfoGetParam.Scope.THREE_YEARS -> minusYears(3) + ActiveInfoGetParam.Scope.FIVE_YEARS -> minusYears(5) + else -> minusWeeks(1) + } + }.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), + LocalDateTime.now(ZoneOffset.UTC).plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + ) + ).map { + ActiveInfoVo.HistoryVo( + LocalDate.parse( + it["time"] as String, + DateTimeFormatter.ofPattern("yyyy-MM-dd") + ), it["count"] as Int + ) + } + + return ActiveInfoVo(getHistory("REGISTER"), getHistory("LOGIN"), getHistory("VERIFY")) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SysLogServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SysLogServiceImpl.kt new file mode 100644 index 0000000..709c560 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/system/impl/SysLogServiceImpl.kt @@ -0,0 +1,64 @@ +package top.fatweb.oxygen.api.service.system.impl + +import com.baomidou.dynamic.datasource.annotation.DS +import com.baomidou.mybatisplus.core.metadata.OrderItem +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.converter.system.SysLogConverter +import top.fatweb.oxygen.api.entity.permission.User +import top.fatweb.oxygen.api.entity.system.SysLog +import top.fatweb.oxygen.api.mapper.system.SysLogMapper +import top.fatweb.oxygen.api.param.system.SysLogGetParam +import top.fatweb.oxygen.api.service.permission.IUserService +import top.fatweb.oxygen.api.service.system.ISysLogService +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.system.SysLogVo + +/** + * System log service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see SysLogMapper + * @see SysLog + * @see ISysLogService + */ +@DS("sqlite") +@Service +class SysLogServiceImpl( + private val userService: IUserService +) : ServiceImpl(), ISysLogService { + override fun getPage(sysLogGetParam: SysLogGetParam?): PageVo { + val sysLogPage = Page(sysLogGetParam?.currentPage ?: 1, sysLogGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(sysLogGetParam, sysLogPage, OrderItem.desc("start_time")) + + val sysLogIPage = baseMapper.selectPage( + sysLogPage, + sysLogGetParam?.logType?.split(","), + sysLogGetParam?.requestMethod?.split(","), + sysLogGetParam?.searchRequestUrl, + sysLogGetParam?.searchStartTime, + sysLogGetParam?.searchEndTime + ) + + if (sysLogIPage.records.isNotEmpty()) { + val userIds = sysLogIPage.records.map { it.operateUserId } + + userService.list(KtQueryWrapper(User()).select(User::id, User::username).`in`(User::id, userIds)) + .forEach { user -> + sysLogIPage.records.forEach { + if (it.operateUserId == user.id) { + it.operateUsername = user.username + } + } + } + } + + return SysLogConverter.sysLogPageToSysLogPageVo(sysLogIPage) + } +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IEditService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IEditService.kt new file mode 100644 index 0000000..32c7f60 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IEditService.kt @@ -0,0 +1,159 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolCreateParam +import top.fatweb.oxygen.api.param.tool.ToolUpdateParam +import top.fatweb.oxygen.api.param.tool.ToolUpgradeParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool edit service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Tool + */ +interface IEditService : IService { + /** + * Get tool template as list by platform + * + * @return List of ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + * @see ToolTemplateVo + */ + fun getTemplate(platform: Platform): List + + /** + * Get tool template by ID + * + * @param id ID + * @return ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateVo + */ + fun getTemplate(id: Long): ToolTemplateVo + + /** + * Get tool category as list + * + * @return List of ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryVo + */ + fun getCategory(): List + + /** + * Get tool by ID + * + * @param id Tool ID + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getOne(id: Long): ToolVo + + /** + * Create tool + * + * @param toolCreateParam Create tool parameters + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCreateParam + * @see ToolVo + */ + fun create(toolCreateParam: ToolCreateParam): ToolVo + + /** + * Upgrade tool + * + * @param toolUpgradeParam Upgrade tool parameters + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolUpgradeParam + * @see ToolVo + */ + fun upgrade(toolUpgradeParam: ToolUpgradeParam): ToolVo + + /** + * Update tool + * + * @param toolUpdateParam Update tool parameters + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolUpdateParam + * @see ToolVo + */ + fun update(toolUpdateParam: ToolUpdateParam): ToolVo + + /** + * Get personal tools + * + * @param pageSortParam Page sort parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + * @see PageVo + * @see ToolVo + */ + fun getPage(pageSortParam: PageSortParam): PageVo + + /** + * Get tool detail + * + * @param username Username + * @param toolId Tool ID + * @param ver Version + * @param platform Platform + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + * @see ToolVo + */ + fun detail(username: String, toolId: String, ver: String, platform: Platform): ToolVo + + /** + * Submit tool review + * + * @param id Tool ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun submit(id: Long): Boolean + + /** + * Cancel tool review + * + * @param id Tool ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun cancel(id: Long): Boolean + + /** + * Delete tool + * + * @param id Tool ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IManagementService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IManagementService.kt new file mode 100644 index 0000000..2ec08a7 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IManagementService.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.param.tool.ToolManagementGetParam +import top.fatweb.oxygen.api.param.tool.ToolManagementPassParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool management service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Tool + */ +interface IManagementService : IService { + /** + * Get tool by ID + * + * @param id Tool ID + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolVo + */ + fun getOne(id: Long): ToolVo + + /** + * Get tool in page + * + * @param toolManagementGetParam Get tool parameters in tool management + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolManagementGetParam + * @see PageVo + * @see ToolVo + */ + fun getPage(toolManagementGetParam: ToolManagementGetParam?): PageVo + + /** + * Pass tool review + * + * @param id Tool ID + * @param toolManagementPassParam Pass tool review parameters in tool management + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolManagementPassParam + * @see ToolVo + */ + fun pass(id: Long, toolManagementPassParam: ToolManagementPassParam): ToolVo + + /** + * Reject tool review + * + * @param id Tool ID + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolVo + */ + fun reject(id: Long): ToolVo + + /** + * Off shelve tool + * + * @param id Tool ID + * @return ToolVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolVo + */ + fun offShelve(id: Long): ToolVo + + /** + * Delete tool + * + * @param id Tool ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IRToolCategoryService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IRToolCategoryService.kt new file mode 100644 index 0000000..4fc46c5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IRToolCategoryService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.RToolCategory + +/** + * Tool category intermediate service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see RToolCategory + */ +interface IRToolCategoryService : IService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IStoreService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IStoreService.kt new file mode 100644 index 0000000..f5a3337 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IStoreService.kt @@ -0,0 +1,80 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteAddParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteRemoveParam +import top.fatweb.oxygen.api.param.tool.ToolStoreGetParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool store service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see Tool + */ +interface IStoreService : IService { + /** + * Get tool in page + * + * @param toolStoreGetParam Get tool parameters in tool store + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolStoreGetParam + * @see PageVo + * @see ToolVo + */ + fun getPage(toolStoreGetParam: ToolStoreGetParam): PageVo + + /** + * Get tool by username in page + * + * @param pageSortParam Page sort parameters + * @param username Username + * @return PageVo + + /*** + * Add favorite + * + * @param toolFavoriteAddParam Add favorite tool parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolFavoriteAddParam + */ + fun addFavorite(toolFavoriteAddParam: ToolFavoriteAddParam) + + /*** + * Remove favorite tool + * + * @param toolFavoriteRemoveParam Remove favorite tool parameters + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolFavoriteRemoveParam + */ + fun removeFavorite(toolFavoriteRemoveParam: ToolFavoriteRemoveParam) + + /** + * Get favorite tool + * + * @param pageSortParam Page sort parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see PageSortParam + * @see PageVo + * @see ToolVo + */ + fun getFavorite(pageSortParam: PageSortParam): PageVo +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolBaseService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolBaseService.kt new file mode 100644 index 0000000..9a769a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolBaseService.kt @@ -0,0 +1,87 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.ToolBase +import top.fatweb.oxygen.api.param.tool.ToolBaseAddParam +import top.fatweb.oxygen.api.param.tool.ToolBaseGetParam +import top.fatweb.oxygen.api.param.tool.ToolBaseUpdateParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolBaseVo + +/** + * Tool base service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see ToolBase + */ +interface IToolBaseService : IService { + /** + * Get tool base by ID + * + * @param id ID + * @return ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseVo + */ + fun getOne(id: Long): ToolBaseVo + + /** + * Get tool base in page + * + * @param toolBaseGetParam Get tool base parameters + * @return PageVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseGetParam + * @see PageVo + * @see ToolBaseVo + */ + fun get(toolBaseGetParam: ToolBaseGetParam?): PageVo + + /** + * Get all tool base in list + * + * @return List of ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseVo + */ + fun getList(): List + + /** + * Add tool base + * + * @param toolBaseAddParam Add tool base parameters + * @return ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseAddParam + * @see ToolBaseVo + */ + fun add(toolBaseAddParam: ToolBaseAddParam): ToolBaseVo + + /** + * Update tool base + * + * @param toolBaseUpdateParam Update tool base parameters + * @return ToolBaseVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolBaseUpdateParam + * @see ToolBaseVo + */ + fun update(toolBaseUpdateParam: ToolBaseUpdateParam): ToolBaseVo + + /** + * Delete tool base + * + * @param id ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolCategoryService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolCategoryService.kt new file mode 100644 index 0000000..7b2e3a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolCategoryService.kt @@ -0,0 +1,72 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.ToolCategory +import top.fatweb.oxygen.api.param.tool.ToolCategoryAddParam +import top.fatweb.oxygen.api.param.tool.ToolCategoryUpdateParam +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo + +/** + * Tool category service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see ToolCategory + */ +interface IToolCategoryService : IService { + /** + * Get tool category by ID + * + * @param id ID + * @return ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryVo + */ + fun getOne(id: Long): ToolCategoryVo + + /** + * Get tool category in list + * + * @return List of ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryVo + */ + fun get(): List + + /** + * Add tool category + * + * @param toolCategoryAddParam Add tool category parameters + * @return ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryAddParam + * @see ToolCategoryVo + */ + fun add(toolCategoryAddParam: ToolCategoryAddParam): ToolCategoryVo + + /** + * Update tool category + * + * @param toolCategoryUpdateParam Update tool category parameters + * @return ToolCategoryVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryUpdateParam + * @see ToolCategoryVo + */ + fun update(toolCategoryUpdateParam: ToolCategoryUpdateParam): ToolCategoryVo + + /** + * Delete tool category + * + * @param id ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolDataService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolDataService.kt new file mode 100644 index 0000000..b8e9adc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolDataService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.ToolData + +/** + * Tool data service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see ToolData + */ +interface IToolDataService : IService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolFavoriteService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolFavoriteService.kt new file mode 100644 index 0000000..108b50c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolFavoriteService.kt @@ -0,0 +1,14 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.ToolFavorite + +/** + * Tool favorite service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see ToolFavorite + */ +interface IToolFavoriteService : IService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolTemplateService.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolTemplateService.kt new file mode 100644 index 0000000..be6d60e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/IToolTemplateService.kt @@ -0,0 +1,76 @@ +package top.fatweb.oxygen.api.service.tool + +import com.baomidou.mybatisplus.extension.service.IService +import top.fatweb.oxygen.api.entity.tool.ToolTemplate +import top.fatweb.oxygen.api.param.tool.ToolTemplateAddParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateGetParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateUpdateParam +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo + +/** + * Tool template service interface + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IService + * @see ToolTemplate + */ +interface IToolTemplateService : IService { + /** + * Get tool template by ID + * + * @param id ID + * @return ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateVo + */ + fun getOne(id: Long): ToolTemplateVo + + /** + * Get tool template in page + * + * @return Page of ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateGetParam + * @see PageVo + * @see ToolTemplateVo + */ + fun get(toolTemplateGetParam: ToolTemplateGetParam?): PageVo + + /** + * Add tool template + * + * @param toolTemplateAddParam Add tool template parameters + * @return ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateAddParam + * @see ToolTemplateVo + */ + fun add(toolTemplateAddParam: ToolTemplateAddParam): ToolTemplateVo + + /** + * Update tool template + * + * @param toolTemplateUpdateParam Update tool template parameters + * @return ToolTemplateVo object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolTemplateUpdateParam + * @see ToolTemplateVo + */ + fun update(toolTemplateUpdateParam: ToolTemplateUpdateParam): ToolTemplateVo + + /** + * Delete tool template + * + * @param id ID + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delete(id: Long): Boolean +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/EditServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/EditServiceImpl.kt new file mode 100644 index 0000000..e1b161b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/EditServiceImpl.kt @@ -0,0 +1,274 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.dao.DuplicateKeyException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.tool.ToolCategoryConverter +import top.fatweb.oxygen.api.converter.tool.ToolConverter +import top.fatweb.oxygen.api.converter.tool.ToolTemplateConverter +import top.fatweb.oxygen.api.entity.tool.* +import top.fatweb.oxygen.api.exception.* +import top.fatweb.oxygen.api.mapper.tool.EditMapper +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolCreateParam +import top.fatweb.oxygen.api.param.tool.ToolUpdateParam +import top.fatweb.oxygen.api.param.tool.ToolUpgradeParam +import top.fatweb.oxygen.api.service.tool.* +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool edit service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see EditMapper + * @see Tool + * @see IEditService + */ +@Service +class EditServiceImpl( + private val toolTemplateService: IToolTemplateService, + private val toolCategoryService: IToolCategoryService, + private val toolDataService: IToolDataService, + private val rToolCategoryService: IRToolCategoryService +) : ServiceImpl(), IEditService { + override fun getTemplate(platform: Platform): List = + toolTemplateService.list( + KtQueryWrapper(ToolTemplate()) + .eq(ToolTemplate::platform, platform) + .eq(ToolTemplate::enable, 1) + ).map(ToolTemplateConverter::toolTemplateToToolTemplateVoByList) + + override fun getTemplate(id: Long): ToolTemplateVo = + baseMapper.selectTemplate(id)?.let(ToolTemplateConverter::toolTemplateToToolTemplateVoWithBaseDist) + ?: throw NoRecordFoundException() + + override fun getCategory(): List = + toolCategoryService.list(KtQueryWrapper(ToolCategory()).eq(ToolCategory::enable, 1)) + .map(ToolCategoryConverter::toolCategoryToToolCategoryVo) + + override fun getOne(id: Long): ToolVo = + baseMapper.selectOne(id, WebUtil.getLoginUserId()!!) + ?.let(ToolConverter::toolToToolVo) ?: throw NoRecordFoundException() + + @Transactional + override fun create(toolCreateParam: ToolCreateParam): ToolVo { + val template = this.getTemplate(toolCreateParam.templateId!!) + baseMapper.selectOne( + KtQueryWrapper(Tool()) + .eq(Tool::toolId, toolCreateParam.toolId!!) + .eq(Tool::authorId, WebUtil.getLoginUserId()!!) + .eq(Tool::platform, template.platform) + )?.let { + throw DuplicateKeyException("Duplicate Key") + } + val newSource = ToolData().apply { data = template.source!!.data } + val newDist = ToolData().apply { data = "" } + toolDataService.saveBatch(listOf(newSource, newDist)) + + val tool = Tool().apply { + name = toolCreateParam.name!!.trim() + toolId = toolCreateParam.toolId + icon = toolCreateParam.icon + platform = template.platform + description = toolCreateParam.description + baseId = template.base!!.id + authorId = WebUtil.getLoginUserId()!! + ver = toolCreateParam.ver!!.split(".").map(String::toLong).joinToString(".") + keywords = toolCreateParam.keywords + sourceId = newSource.id + distId = newDist.id + entryPoint = template.entryPoint + } + + this.save(tool) + + toolCreateParam.categories!!.forEach { + toolCategoryService.getById(it) ?: throw NoRecordFoundException() + rToolCategoryService.save(RToolCategory().apply { + toolId = tool.id + categoryId = it + }) + } + + return this.getOne(tool.id!!) + } + + @Transactional + override fun upgrade(toolUpgradeParam: ToolUpgradeParam): ToolVo { + val originalTool = this.detail("!", toolUpgradeParam.toolId!!, "latest", toolUpgradeParam.platform!!) + if (originalTool.review == Tool.ReviewType.PROCESSING) { + throw ToolUnderReviewException() + } + if (originalTool.review != Tool.ReviewType.PASS || originalTool.publish == 0L) { + throw ToolHasUnpublishedVersionException() + } + + val originalVersion = originalTool.ver!! + val originalVersionNumberList = originalVersion.split(".").map(String::toLong) + val newVersionNumberList = toolUpgradeParam.ver!!.split(".").map(String::toLong) + if (!newVersionNumberList.foldIndexed(false) { index: Int, acc: Boolean, version: Long -> + if (!acc && originalVersionNumberList[index] > version) { + throw IllegalVersionException() + } + if (originalVersionNumberList[index] < version) true else acc + }) { + throw IllegalVersionException() + } + + val newSource = ToolData().apply { data = originalTool.source!!.data } + val newDist = ToolData().apply { data = "" } + toolDataService.saveBatch(listOf(newSource, newDist)) + + val tool = Tool().apply { + name = originalTool.name!! + toolId = originalTool.toolId + icon = originalTool.icon + platform = originalTool.platform + description = originalTool.description + baseId = originalTool.base!!.id + authorId = WebUtil.getLoginUserId()!! + ver = newVersionNumberList.joinToString(".") + keywords = originalTool.keywords + sourceId = newSource.id + distId = newDist.id + entryPoint = originalTool.entryPoint + } + + this.save(tool) + + originalTool.categories!!.forEach { + toolCategoryService.getById(it.id) ?: throw NoRecordFoundException() + rToolCategoryService.save(RToolCategory().apply { + toolId = tool.id + categoryId = it.id + }) + } + + return this.getOne(tool.id!!) + } + + @Transactional + override fun update(toolUpdateParam: ToolUpdateParam): ToolVo { + val tool = getById(toolUpdateParam.id) + if (tool.review == Tool.ReviewType.PROCESSING) { + throw ToolUnderReviewException() + } + if (tool.review == Tool.ReviewType.PASS || tool.publish != 0L) { + throw ToolHasBeenPublishedException() + } + + this.updateById(Tool().apply { + id = toolUpdateParam.id + name = toolUpdateParam.name + icon = toolUpdateParam.icon + description = toolUpdateParam.description + keywords = toolUpdateParam.keywords + }) + + if (!toolUpdateParam.categories.isNullOrEmpty()) { + val oldCategories = rToolCategoryService.list( + KtQueryWrapper(RToolCategory()).select(RToolCategory::categoryId).eq(RToolCategory::toolId, tool.id) + ).map(RToolCategory::categoryId) + val addCategories = HashSet() + val removeCategories = HashSet() + toolUpdateParam.categories.forEach(addCategories::add) + oldCategories.forEach { + it?.let(removeCategories::add) + } + removeCategories.removeAll(addCategories) + oldCategories.toSet().let(addCategories::removeAll) + + removeCategories.forEach { + rToolCategoryService.remove( + KtQueryWrapper(RToolCategory()).eq( + RToolCategory::toolId, tool.id + ).eq(RToolCategory::categoryId, it) + ) + } + + addCategories.forEach { + rToolCategoryService.save(RToolCategory().apply { + toolId = tool.id + categoryId = it + }) + } + } + + if (!toolUpdateParam.source.isNullOrBlank()) { + toolDataService.updateById(ToolData().apply { + id = tool.sourceId + data = toolUpdateParam.source + }) + } + + return this.getOne(tool.id!!) + } + + override fun getPage(pageSortParam: PageSortParam): PageVo { + val toolIdsPage = Page(pageSortParam.currentPage, 20) + toolIdsPage.setOptimizeCountSql(false) + + val toolIdsIPage = baseMapper.selectPersonalToolIdPage(toolIdsPage, WebUtil.getLoginUserId()!!) + val toolPage = Page(toolIdsIPage.current, toolIdsIPage.size, toolIdsIPage.total) + if (toolIdsIPage.total > 0) { + toolPage.setRecords(baseMapper.selectListByToolIds(toolIdsIPage.records, WebUtil.getLoginUserId()!!)) + } + + return ToolConverter.toolPageToToolPageVo(toolPage) + } + + override fun detail(username: String, toolId: String, ver: String, platform: Platform): ToolVo { + if (username == "!" && WebUtil.getLoginUserId() == null) { + throw NoRecordFoundException() + } + + return baseMapper.selectDetail(username, toolId, ver, platform, WebUtil.getLoginUsername()) + ?.let(ToolConverter::toolToToolVo) ?: throw NoRecordFoundException() + } + + override fun submit(id: Long): Boolean { + val tool = getById(id) + if (tool.review == Tool.ReviewType.PROCESSING) { + throw ToolUnderReviewException() + } + if (tool.review == Tool.ReviewType.PASS && tool.publish != 0L) { + throw ToolHasBeenPublishedException() + } + + return update(KtUpdateWrapper(Tool()).eq(Tool::id, id).set(Tool::review, Tool.ReviewType.PROCESSING)) + } + + override fun cancel(id: Long): Boolean { + val tool = getById(id) + if (tool.review == Tool.ReviewType.PASS && tool.publish != 0L) { + throw ToolHasBeenPublishedException() + } + if (tool.review != Tool.ReviewType.PROCESSING) { + throw ToolNotUnderReviewException() + } + + return update(KtUpdateWrapper(Tool()).eq(Tool::id, id).set(Tool::review, Tool.ReviewType.NONE)) + } + + @Transactional + override fun delete(id: Long): Boolean { + val tool = baseMapper.selectOne( + KtQueryWrapper(Tool()).eq(Tool::id, id) + .eq(Tool::authorId, WebUtil.getLoginUserId()!!) + ) ?: throw NoRecordFoundException() + + toolDataService.removeBatchByIds(listOf(tool.sourceId, tool.distId)) + rToolCategoryService.remove(KtQueryWrapper(RToolCategory()).eq(RToolCategory::toolId, tool.id)) + return this.removeById(id) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ManagementServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ManagementServiceImpl.kt new file mode 100644 index 0000000..4810eae --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ManagementServiceImpl.kt @@ -0,0 +1,128 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.tool.ToolConverter +import top.fatweb.oxygen.api.entity.tool.RToolCategory +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.entity.tool.ToolData +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.exception.ToolHasNotBeenPublishedException +import top.fatweb.oxygen.api.exception.ToolNotUnderReviewException +import top.fatweb.oxygen.api.mapper.tool.ManagementMapper +import top.fatweb.oxygen.api.param.tool.ToolManagementGetParam +import top.fatweb.oxygen.api.param.tool.ToolManagementPassParam +import top.fatweb.oxygen.api.service.tool.IManagementService +import top.fatweb.oxygen.api.service.tool.IRToolCategoryService +import top.fatweb.oxygen.api.service.tool.IToolDataService +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo +import java.time.LocalDateTime +import java.time.ZoneOffset + +/** + * Tool management service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolDataService + * @see IRToolCategoryService + * @see ServiceImpl + * @see ManagementMapper + * @see Tool + * @see IManagementService + */ +@Service +class ManagementServiceImpl( + private val toolDataService: IToolDataService, + private val rToolCategoryService: IRToolCategoryService +) : ServiceImpl(), IManagementService { + override fun getOne(id: Long): ToolVo = + baseMapper.selectOne(id) + ?.let(ToolConverter::toolToToolVo) ?: throw NoRecordFoundException() + + override fun getPage(toolManagementGetParam: ToolManagementGetParam?): PageVo { + val toolIdsPage = Page(toolManagementGetParam?.currentPage ?: 1, toolManagementGetParam?.pageSize ?: 20) + toolIdsPage.setOptimizeCountSql(false) + + val toolIdsIPage = + baseMapper.selectPage( + toolIdsPage, + toolManagementGetParam?.review?.split(","), + toolManagementGetParam?.platform?.split(","), + toolManagementGetParam?.searchType ?: "ALL", + toolManagementGetParam?.searchValue, + toolManagementGetParam?.searchRegex ?: false + ) + val toolPage = Page(toolIdsIPage.current, toolIdsIPage.size, toolIdsIPage.total) + if (toolIdsIPage.total > 0) { + toolPage.setRecords(baseMapper.selectListByIds(toolIdsIPage.records)) + } + + return ToolConverter.toolPageToToolPageVo(toolPage) + } + + override fun pass(id: Long, toolManagementPassParam: ToolManagementPassParam): ToolVo { + val tool = this.getById(id) ?: throw NoRecordFoundException() + if (tool.review !== Tool.ReviewType.PROCESSING) { + throw ToolNotUnderReviewException() + } + + toolDataService.update( + KtUpdateWrapper(ToolData()).eq(ToolData::id, tool.distId).set(ToolData::data, toolManagementPassParam.dist) + ) + + this.update( + KtUpdateWrapper(Tool()) + .eq(Tool::id, id) + .set(Tool::review, Tool.ReviewType.PASS) + .set(Tool::publish, LocalDateTime.now(ZoneOffset.UTC).toInstant(ZoneOffset.UTC).toEpochMilli()) + ) + + return this.getOne(id) + } + + override fun reject(id: Long): ToolVo { + val tool = this.getById(id) ?: throw NoRecordFoundException() + if (tool.review !== Tool.ReviewType.PROCESSING) { + throw ToolNotUnderReviewException() + } + + this.update( + KtUpdateWrapper(Tool()) + .eq(Tool::id, id) + .set(Tool::review, Tool.ReviewType.REJECT) + ) + + return this.getOne(id) + } + + override fun offShelve(id: Long): ToolVo { + val tool = this.getById(id) ?: throw NoRecordFoundException() + if (tool.review !== Tool.ReviewType.PASS && tool.publish == 0L) { + throw ToolHasNotBeenPublishedException() + } + + this.update( + KtUpdateWrapper(Tool()) + .eq(Tool::id, id) + .set(Tool::review, Tool.ReviewType.REJECT) + ) + + return this.getOne(id) + } + + @Transactional + override fun delete(id: Long): Boolean { + val tool = this.getById(id) ?: throw NoRecordFoundException() + + toolDataService.removeBatchByIds(listOf(tool.sourceId, tool.distId)) + rToolCategoryService.remove(KtQueryWrapper(RToolCategory()).eq(RToolCategory::toolId, id)) + + return this.removeById(id) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/RToolCategoryServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/RToolCategoryServiceImpl.kt new file mode 100644 index 0000000..87844df --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/RToolCategoryServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.tool.RToolCategory +import top.fatweb.oxygen.api.mapper.tool.RToolCategoryMapper +import top.fatweb.oxygen.api.service.tool.IRToolCategoryService + +/** + * Tool category service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see RToolCategoryMapper + * @see RToolCategory + * @see IRToolCategoryService + */ +@Service +class RToolCategoryServiceImpl : ServiceImpl(), IRToolCategoryService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/StoreServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/StoreServiceImpl.kt new file mode 100644 index 0000000..5bd7743 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/StoreServiceImpl.kt @@ -0,0 +1,131 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.tool.ToolConverter +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.entity.tool.ToolFavorite +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.exception.RecordAlreadyExists +import top.fatweb.oxygen.api.mapper.tool.StoreMapper +import top.fatweb.oxygen.api.param.PageSortParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteAddParam +import top.fatweb.oxygen.api.param.tool.ToolFavoriteRemoveParam +import top.fatweb.oxygen.api.param.tool.ToolStoreGetParam +import top.fatweb.oxygen.api.service.tool.IStoreService +import top.fatweb.oxygen.api.service.tool.IToolFavoriteService +import top.fatweb.oxygen.api.util.WebUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolVo + +/** + * Tool store service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see StoreMapper + * @see Tool + * @see IStoreService + */ +@Service +class StoreServiceImpl( + private val toolFavoriteService: IToolFavoriteService +) : ServiceImpl(), IStoreService { + override fun getPage(toolStoreGetParam: ToolStoreGetParam): PageVo { + val toolIdsPage = Page(toolStoreGetParam.currentPage, 20) + toolIdsPage.setOptimizeCountSql(false) + + val toolIdsIPage = + baseMapper.selectAuthorToolIdPage(toolIdsPage, toolStoreGetParam.searchValue, toolStoreGetParam.platform) + val toolPage = Page(toolIdsIPage.current, toolIdsIPage.size, toolIdsIPage.total) + if (toolIdsIPage.total > 0) { + toolPage.setRecords(baseMapper.selectListByAuthorToolIds(toolIdsIPage.records, WebUtil.getLoginUserId(), toolStoreGetParam.platform)) + } + + return ToolConverter.toolPageToToolPageVo(toolPage) + } + + override fun getPage(pageSortParam: PageSortParam, username: String): PageVo { + val toolIdsPage = Page(pageSortParam.currentPage, 20) + toolIdsPage.setOptimizeCountSql(false) + + val toolIdsIPage = baseMapper.selectAuthorToolIdPageByUsername(toolIdsPage, username) + val toolPage = Page(toolIdsIPage.current, toolIdsIPage.size, toolIdsIPage.total) + if (toolIdsIPage.total > 0) { + toolPage.setRecords(baseMapper.selectListByAuthorToolIds(toolIdsIPage.records, WebUtil.getLoginUserId())) + } + + return ToolConverter.toolPageToToolPageVo(toolPage) + } + + @Transactional + override fun addFavorite(toolFavoriteAddParam: ToolFavoriteAddParam) { + if (toolFavoriteAddParam.authorId == WebUtil.getLoginUserId()) { + throw NoRecordFoundException() + } + + if (toolFavoriteService.exists( + KtQueryWrapper(ToolFavorite()) + .eq(ToolFavorite::userId, WebUtil.getLoginUserId()) + .eq(ToolFavorite::authorId, toolFavoriteAddParam.authorId) + .eq(ToolFavorite::toolId, toolFavoriteAddParam.toolId) + ) + ) { + throw RecordAlreadyExists() + } + + if (baseMapper.countPublishedToolByAuthorAndToolId( + toolFavoriteAddParam.authorId!!, + toolFavoriteAddParam.toolId!! + ) <= 0 + ) { + throw NoRecordFoundException() + } + + toolFavoriteService.save( + ToolFavorite().apply { + userId = WebUtil.getLoginUserId() + authorId = toolFavoriteAddParam.authorId + toolId = toolFavoriteAddParam.toolId + } + ) + } + + @Transactional + override fun removeFavorite(toolFavoriteRemoveParam: ToolFavoriteRemoveParam) { + if (!toolFavoriteService.remove( + KtQueryWrapper(ToolFavorite()) + .eq(ToolFavorite::userId, WebUtil.getLoginUserId()) + .eq(ToolFavorite::authorId, toolFavoriteRemoveParam.authorId) + .eq(ToolFavorite::toolId, toolFavoriteRemoveParam.toolId) + ) + ) { + throw NoRecordFoundException() + } + } + + override fun getFavorite(pageSortParam: PageSortParam): PageVo { + val toolFavoritePage = Page(pageSortParam.currentPage, 20) + + val toolFavoriteIPage = toolFavoriteService.page( + toolFavoritePage, + KtQueryWrapper(ToolFavorite()).eq(ToolFavorite::userId, WebUtil.getLoginUserId()) + ) + + val toolPage = Page(toolFavoriteIPage.current, toolFavoriteIPage.size, toolFavoriteIPage.total) + if (toolFavoriteIPage.total > 0) { + toolPage.setRecords( + baseMapper.selectListByAuthorToolIds( + toolFavoriteIPage.records.map { "${it.authorId}:${it.toolId}" }, + WebUtil.getLoginUserId() + ) + ) + } + + return ToolConverter.toolPageToToolPageVo(toolPage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolBaseServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolBaseServiceImpl.kt new file mode 100644 index 0000000..237ba10 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolBaseServiceImpl.kt @@ -0,0 +1,118 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.tool.ToolBaseConverter +import top.fatweb.oxygen.api.entity.tool.ToolBase +import top.fatweb.oxygen.api.entity.tool.ToolData +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.mapper.tool.ToolBaseMapper +import top.fatweb.oxygen.api.param.tool.ToolBaseAddParam +import top.fatweb.oxygen.api.param.tool.ToolBaseGetParam +import top.fatweb.oxygen.api.param.tool.ToolBaseUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolBaseService +import top.fatweb.oxygen.api.service.tool.IToolDataService +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolBaseVo + +/** + * Tool base service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolDataService + * @see ServiceImpl + * @see ToolBaseMapper + * @see ToolBase + * @see IToolBaseService + */ +@Service +class ToolBaseServiceImpl( + private val toolDataService: IToolDataService +) : ServiceImpl(), IToolBaseService { + override fun getOne(id: Long): ToolBaseVo = + baseMapper.selectOne(id)?.let(ToolBaseConverter::toolBaseToToolBaseVo) ?: throw NoRecordFoundException() + + override fun get(toolBaseGetParam: ToolBaseGetParam?): PageVo { + val basePage = Page(toolBaseGetParam?.currentPage ?: 1, toolBaseGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(toolBaseGetParam, basePage) + + return ToolBaseConverter.toolBasePageToToolBasePageVo( + this.page( + basePage, + KtQueryWrapper(ToolBase()).`in`( + !toolBaseGetParam?.platform.isNullOrBlank(), + ToolBase::platform, + toolBaseGetParam?.platform?.split(",") + ) + ) + ) + } + + override fun getList(): List = this.list().map(ToolBaseConverter::toolBaseToToolBaseVoByGetList) + + @Transactional + override fun add(toolBaseAddParam: ToolBaseAddParam): ToolBaseVo { + val newSource = ToolData().apply { data = "" } + val newDist = ToolData().apply { data = "" } + + toolDataService.saveBatch(listOf(newSource, newDist)) + + val toolBase = ToolBase().apply { + name = toolBaseAddParam.name + sourceId = newSource.id + distId = newDist.id + source = newSource + dist = newDist + platform = toolBaseAddParam.platform + } + + this.save(toolBase) + + return ToolBaseConverter.toolBaseToToolBaseVo(toolBase) + } + + @Transactional + override fun update(toolBaseUpdateParam: ToolBaseUpdateParam): ToolBaseVo { + val toolBase = baseMapper.selectOne(toolBaseUpdateParam.id!!) ?: throw NoRecordFoundException() + + var hasCompiled: Int? = null + + if (!toolBaseUpdateParam.source.isNullOrBlank()) { + toolDataService.updateById(ToolData().apply { + id = toolBase.sourceId + data = toolBaseUpdateParam.source + }) + hasCompiled = 0 + } + + if (!toolBaseUpdateParam.dist.isNullOrBlank()) { + toolDataService.updateById(ToolData().apply { + id = toolBase.distId + data = toolBaseUpdateParam.dist + }) + hasCompiled = 1 + } + + this.updateById(ToolBase().apply { + id = toolBaseUpdateParam.id + name = toolBaseUpdateParam.name + compiled = hasCompiled + }) + + return this.getOne(toolBase.id!!) + } + + @Transactional + override fun delete(id: Long): Boolean { + val toolBase = this.getById(id) + + return toolDataService.removeBatchByIds(listOf(toolBase.sourceId, toolBase.distId)) + && this.removeById(id) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolCategoryServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolCategoryServiceImpl.kt new file mode 100644 index 0000000..ed11c75 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolCategoryServiceImpl.kt @@ -0,0 +1,55 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.converter.tool.ToolCategoryConverter +import top.fatweb.oxygen.api.entity.tool.ToolCategory +import top.fatweb.oxygen.api.exception.DatabaseInsertException +import top.fatweb.oxygen.api.exception.DatabaseUpdateException +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.mapper.tool.ToolCategoryMapper +import top.fatweb.oxygen.api.param.tool.ToolCategoryAddParam +import top.fatweb.oxygen.api.param.tool.ToolCategoryUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolCategoryService +import top.fatweb.oxygen.api.vo.tool.ToolCategoryVo + +/** + * Tool category service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see ToolCategoryMapper + * @see ToolCategory + * @see IToolCategoryService + */ +@Service +class ToolCategoryServiceImpl : ServiceImpl(), IToolCategoryService { + override fun getOne(id: Long): ToolCategoryVo = + this.getById(id)?.let(ToolCategoryConverter::toolCategoryToToolCategoryVo) ?: throw NoRecordFoundException() + + override fun get(): List = + this.list().map(ToolCategoryConverter::toolCategoryToToolCategoryVo) + + override fun add(toolCategoryAddParam: ToolCategoryAddParam): ToolCategoryVo { + val toolCategory = ToolCategoryConverter.toolCategoryAddParamToToolCategory(toolCategoryAddParam) + + if (!this.save(toolCategory)) { + throw DatabaseInsertException() + } + + return ToolCategoryConverter.toolCategoryToToolCategoryVo(toolCategory) + } + + override fun update(toolCategoryUpdateParam: ToolCategoryUpdateParam): ToolCategoryVo { + val toolCategory = ToolCategoryConverter.toolCategoryUpdateParamToToolCategory(toolCategoryUpdateParam) + + if (!this.updateById(toolCategory)) { + throw DatabaseUpdateException() + } + + return ToolCategoryConverter.toolCategoryToToolCategoryVo(toolCategory) + } + + override fun delete(id: Long): Boolean = this.removeById(id) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolDataServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolDataServiceImpl.kt new file mode 100644 index 0000000..74cc3ca --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolDataServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.tool.ToolData +import top.fatweb.oxygen.api.mapper.tool.ToolDataMapper +import top.fatweb.oxygen.api.service.tool.IToolDataService + +/** + * Tool data service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see ToolDataMapper + * @see ToolData + * @see IToolDataService + */ +@Service +class ToolDataServiceImpl : ServiceImpl(), IToolDataService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolFavoriteServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolFavoriteServiceImpl.kt new file mode 100644 index 0000000..8bb5b55 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolFavoriteServiceImpl.kt @@ -0,0 +1,20 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import top.fatweb.oxygen.api.entity.tool.ToolFavorite +import top.fatweb.oxygen.api.mapper.tool.ToolFavoriteMapper +import top.fatweb.oxygen.api.service.tool.IToolFavoriteService + +/** + * Tool favorite service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ServiceImpl + * @see ToolFavoriteMapper + * @see ToolFavorite + * @see IToolFavoriteService + */ +@Service +class ToolFavoriteServiceImpl : ServiceImpl(), IToolFavoriteService \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolTemplateServiceImpl.kt b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolTemplateServiceImpl.kt new file mode 100644 index 0000000..34195b9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/service/tool/impl/ToolTemplateServiceImpl.kt @@ -0,0 +1,103 @@ +package top.fatweb.oxygen.api.service.tool.impl + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import top.fatweb.oxygen.api.converter.tool.ToolTemplateConverter +import top.fatweb.oxygen.api.entity.tool.ToolData +import top.fatweb.oxygen.api.entity.tool.ToolTemplate +import top.fatweb.oxygen.api.exception.NoRecordFoundException +import top.fatweb.oxygen.api.mapper.tool.ToolTemplateMapper +import top.fatweb.oxygen.api.param.tool.ToolTemplateAddParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateGetParam +import top.fatweb.oxygen.api.param.tool.ToolTemplateUpdateParam +import top.fatweb.oxygen.api.service.tool.IToolBaseService +import top.fatweb.oxygen.api.service.tool.IToolDataService +import top.fatweb.oxygen.api.service.tool.IToolTemplateService +import top.fatweb.oxygen.api.util.PageUtil +import top.fatweb.oxygen.api.vo.PageVo +import top.fatweb.oxygen.api.vo.tool.ToolTemplateVo + +/** + * Tool template service implement + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see IToolDataService + * @see IToolBaseService + * @see ServiceImpl + * @see ToolTemplateMapper + * @see ToolTemplate + * @see IToolTemplateService + */ +@Service +class ToolTemplateServiceImpl( + private val toolDataService: IToolDataService, + private val toolBaseService: IToolBaseService +) : ServiceImpl(), IToolTemplateService { + override fun getOne(id: Long): ToolTemplateVo = + baseMapper.selectOne(id)?.let(ToolTemplateConverter::toolTemplateToToolTemplateVo) + ?: throw NoRecordFoundException() + + override fun get(toolTemplateGetParam: ToolTemplateGetParam?): PageVo { + val templatePage = + Page(toolTemplateGetParam?.currentPage ?: 1, toolTemplateGetParam?.pageSize ?: 20) + + PageUtil.setPageSort(toolTemplateGetParam, templatePage) + + return ToolTemplateConverter.toolTemplatePageToToolTemplatePageVo( + baseMapper.selectListWithBaseName(templatePage, toolTemplateGetParam?.platform?.split(",")) + ) + } + + @Transactional + override fun add(toolTemplateAddParam: ToolTemplateAddParam): ToolTemplateVo { + val toolBase = toolBaseService.getOne(toolTemplateAddParam.baseId!!) + + val newSource = ToolData().apply { data = "" } + + toolDataService.save(newSource) + + val toolTemplate = ToolTemplate().apply { + name = toolTemplateAddParam.name + baseId = toolTemplateAddParam.baseId + sourceId = newSource.id + source = newSource + platform = toolBase.platform + entryPoint = toolTemplateAddParam.entryPoint + enable = if (toolTemplateAddParam.enable) 1 else 0 + } + + this.save(toolTemplate) + + return ToolTemplateConverter.toolTemplateToToolTemplateVo(toolTemplate) + } + + @Transactional + override fun update(toolTemplateUpdateParam: ToolTemplateUpdateParam): ToolTemplateVo { + val toolTemplate = baseMapper.selectOne(toolTemplateUpdateParam.id!!) ?: throw NoRecordFoundException() + + toolDataService.updateById(ToolData().apply { + id = toolTemplate.sourceId + data = toolTemplateUpdateParam.source + }) + + this.updateById(ToolTemplate().apply { + id = toolTemplateUpdateParam.id + name = toolTemplateUpdateParam.name + entryPoint = toolTemplateUpdateParam.entryPoint + enable = toolTemplateUpdateParam.enable?.let { if (it) 1 else 0 } + }) + + return this.getOne(toolTemplate.id!!) + } + + @Transactional + override fun delete(id: Long): Boolean { + val toolTemplate = this.getById(id) + + return toolDataService.removeById(toolTemplate.sourceId) + && this.removeById(id) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/BaseSettings.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/BaseSettings.kt new file mode 100644 index 0000000..4de70bf --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/BaseSettings.kt @@ -0,0 +1,44 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.annotation.JsonInclude + +/** + * Base settings entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +data class BaseSettings( + /** + * Application name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var appName: String? = null, + + /** + * Application URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var appUrl: String? = null, + + /** + * Verify URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var verifyUrl: String? = null, + + /** + * Retrieve URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var retrieveUrl: String? = null +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSecurityType.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSecurityType.kt new file mode 100644 index 0000000..ede4118 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSecurityType.kt @@ -0,0 +1,31 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonValue + +/** + * Type of mail security + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +enum class MailSecurityType(val code: String) { + NONE("None"), + SSL_TLS("SSL/TLS"), + START_TLS("StartTls"); + + @JsonCreator + fun fromCode(code: String): MailSecurityType? { + entries.forEach { + if (it.code == code) { + return it + } + } + return null + } + + @JsonValue + override fun toString(): String { + return code + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSettings.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSettings.kt new file mode 100644 index 0000000..5c2fa27 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/MailSettings.kt @@ -0,0 +1,68 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.annotation.JsonInclude + +/** + * Mail settings entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +data class MailSettings( + /** + * Host + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var host: String? = null, + + /** + * Port + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var port: Int? = null, + + /** + * Security type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var securityType: MailSecurityType? = MailSecurityType.NONE, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var username: String? = null, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var password: String? = null, + + /** + * Sender + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var from: String? = null, + + /** + * Sender name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var fromName: String? = null +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/SettingsOperator.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/SettingsOperator.kt new file mode 100644 index 0000000..7f71dea --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/SettingsOperator.kt @@ -0,0 +1,221 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.databind.exc.MismatchedInputException +import com.fasterxml.jackson.dataformat.yaml.YAMLMapper +import com.fasterxml.jackson.module.kotlin.readValue +import top.fatweb.oxygen.api.util.StrUtil +import java.io.File +import java.io.IOException +import kotlin.reflect.KMutableProperty1 + +/** + * Settings operator + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object SettingsOperator { + private const val SETTINGS_FILE_NAME = "data/config/settings.yml" + + private val yamlMapper = YAMLMapper() + private val settingFile = File(SETTINGS_FILE_NAME) + private lateinit var systemSettings: SystemSettings + + init { + getFromSettingsFile() + } + + /** + * Get settings from settings file + * + * @throws IOException + * @throws MismatchedInputException + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Throws(IOException::class, MismatchedInputException::class) + private fun getFromSettingsFile() { + if (settingFile.isDirectory) { + throw IOException("'${settingFile.absoluteFile}' is a directory, cannot create settings file") + } + + if (!settingFile.isFile) { + if (settingFile.parentFile.isFile) { + throw IOException("'${settingFile.parentFile.absoluteFile}' is a file, cannot create settings file") + } + + if (!settingFile.parentFile.isDirectory) { + if (!settingFile.parentFile.mkdirs()) { + throw IOException("Cannot make directory: ${settingFile.parentFile.absolutePath}") + } + } + settingFile.delete() + settingFile.createNewFile() + } + + if (settingFile.length() == 0L) { + systemSettings = SystemSettings() + saveSettingsToFile() + } + + systemSettings = yamlMapper.readValue(settingFile) + } + + /** + * Save settings to settings file + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + private fun saveSettingsToFile() { + yamlMapper.writeValue(settingFile, systemSettings) + } + + /** + * Set base settings value + * + * @param field Field to set value. e.g. BaseSettings::appName + * @param value Value to set + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see BaseSettings + */ + fun setAppValue(field: KMutableProperty1, value: V?) { + systemSettings.base?.let { + field.set(it, value) + } ?: let { + systemSettings.base = BaseSettings().also { field.set(it, value) } + } + + saveSettingsToFile() + } + + /** + * Get base settings value + * + * @param field Field to get value from. e.g. BaseSettings::appName + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see BaseSettings + */ + fun getAppValue(field: KMutableProperty1): V? = + this.getAppValue(field, null) + + /** + * Get base settings value with default value + * + * @param field Field to get value from. e.g. BaseSettings::appName + * @param default Return default value when setting not found + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see BaseSettings + */ + fun getAppValue(field: KMutableProperty1, default: V): V = + systemSettings.base?.let(field) ?: default + + /** + * Set mail settings value + * + * @param field Field to set value. e.g. MailSettings::host + * @param value Value to set + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see MailSettings + */ + fun setMailValue(field: KMutableProperty1, value: V?) { + systemSettings.mail?.let { + if (field == MailSettings::password) { + getMailValue(MailSettings::password)?.let { password -> + if (StrUtil.md5(password) == value) { + return + } + } + } + field.set(it, value) + } ?: let { + systemSettings.mail = MailSettings().also { field.set(it, value) } + } + + saveSettingsToFile() + } + + /** + * Get value from mail settings + * + * @param field Field to get value from. e.g. MailSettings::host + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see MailSettings + */ + fun getMailValue(field: KMutableProperty1): V? = + this.getMailValue(field, null) + + /** + * Get value from mail settings with default value + * + * @param field Field to get value from. e.g. MailSettings::host + * @param default Return default value when setting not found + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see MailSettings + */ + fun getMailValue(field: KMutableProperty1, default: V): V = + systemSettings.mail?.let(field) ?: default + + /** + * Set two-factor settings value + * + * @param field Field to set value. e.g. TwoFactorSettings::type + * @param value Value to set + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see TwoFactorSettings + */ + fun setTwoFactorValue(field: KMutableProperty1, value: V?) { + systemSettings.twoFactor?.let { + field.set(it, value) + } ?: let { + systemSettings.twoFactor = TwoFactorSettings().also { field.set(it, value) } + } + + saveSettingsToFile() + } + + /** + * Get value from two-factor settings + * + * @param field Field to get value from. e.g. TwoFactorSettings::type + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see TwoFactorSettings + */ + fun getTwoFactorValue(field: KMutableProperty1): V? = + this.getTwoFactorValue(field, null) + + /** + * Get value from two-factor settings with default value + * + * @param field Field to get value from. e.g. TwoFactorSettings::type + * @param default Return default value when setting not found + * @return Value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see KMutableProperty1 + * @see TwoFactorSettings + */ + fun getTwoFactorValue(field: KMutableProperty1, default: V): V = + systemSettings.twoFactor?.let(field) ?: default +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/SystemSettings.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/SystemSettings.kt new file mode 100644 index 0000000..ee2b038 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/SystemSettings.kt @@ -0,0 +1,36 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.annotation.JsonInclude + +/** + * System setting entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +data class SystemSettings( + /** + * Base setting + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var base: BaseSettings? = null, + + /** + * Mail setting + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var mail: MailSettings? = null, + + /** + * Two-factor setting + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var twoFactor: TwoFactorSettings? = null +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/settings/TwoFactorSettings.kt b/src/main/kotlin/top/fatweb/oxygen/api/settings/TwoFactorSettings.kt new file mode 100644 index 0000000..7eac4a5 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/settings/TwoFactorSettings.kt @@ -0,0 +1,28 @@ +package top.fatweb.oxygen.api.settings + +import com.fasterxml.jackson.annotation.JsonInclude + +/** + * Two-factor settings entity + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +data class TwoFactorSettings( + /** + * Issuer + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var issuer: String? = null, + + /** + * Length of secret key + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + var secretKeyLength: Int? = 16 +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/ApiResponseMappingHandlerMapping.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/ApiResponseMappingHandlerMapping.kt new file mode 100644 index 0000000..62afabf --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/ApiResponseMappingHandlerMapping.kt @@ -0,0 +1,22 @@ +package top.fatweb.oxygen.api.util + +import org.springframework.web.servlet.mvc.condition.RequestCondition +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping +import top.fatweb.oxygen.api.annotation.ApiController +import java.lang.reflect.Method + +/** + * Api response mapping handler mapping + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +class ApiResponseMappingHandlerMapping : RequestMappingHandlerMapping() { + + private fun createCondition(clazz: Class<*>): RequestCondition? = + clazz.getAnnotation(ApiController::class.java)?.version?.let { ApiVersionCondition(it) } + + override fun getCustomMethodCondition(method: Method): RequestCondition<*>? = createCondition(method.javaClass) + + override fun getCustomTypeCondition(handlerType: Class<*>): RequestCondition<*>? = createCondition(handlerType) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/ApiVersionCondition.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/ApiVersionCondition.kt new file mode 100644 index 0000000..90411cf --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/ApiVersionCondition.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.util + +import jakarta.servlet.http.HttpServletRequest +import org.springframework.web.servlet.mvc.condition.RequestCondition + +/** + * Api version condition + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +class ApiVersionCondition(private val apiVersion: Int) : RequestCondition { + private val versionPrefixRegex = Regex("/api/v(\\d+)/.*") + + override fun combine(other: ApiVersionCondition): ApiVersionCondition = ApiVersionCondition(other.apiVersion) + + override fun getMatchingCondition(request: HttpServletRequest): ApiVersionCondition? { + versionPrefixRegex.matchAt(request.requestURI, 0)?.let { + if (it.groupValues[1].toInt() >= this.apiVersion) { + return this + } + } + + return null + } + + override fun compareTo(other: ApiVersionCondition, request: HttpServletRequest): Int = + other.apiVersion - this.apiVersion +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/ByteUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/ByteUtil.kt new file mode 100644 index 0000000..c18a46e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/ByteUtil.kt @@ -0,0 +1,166 @@ +package top.fatweb.oxygen.api.util + +import kotlin.math.floor + +/** + * Byte util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object ByteUtil { + private const val BASE = 1024 + + /** + * Byte unit + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + enum class ByteUnit { + /** + * Byte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + B, + + /** + * Kibibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + KiB, + + /** + * Mebibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + MiB, + + /** + * Gibibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + GiB, + + /** + * Tebibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + TiB, + + /** + * Pebibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + PiB, + + /** + * Exbibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + EiB, + + /** + * Zebibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + ZiB, + + /** + * Yobibyte + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + YiB + } + + /** + * Format byte size + * + * @param byteSize ByteSize + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun formatByteSize(byteSize: Long): String { + if (byteSize <= -1) { + return byteSize.toString() + } + + var size = byteSize.toDouble() + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.B) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.KiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.MiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.GiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.TiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.PiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.EiB) + } + + size /= BASE + if (floor(size / BASE) <= 0) { + return format(size, ByteUnit.ZiB) + } + + size /= BASE + return format(size, ByteUnit.YiB) + } + + private fun format(size: Double, unit: ByteUnit): String { + val precision = if (size * 1000 % 10 > 0) { + 3 + } else if (size * 100 % 10 > 0) { + 2 + } else if (size * 10 % 10 > 0) { + 1 + } else { + 0 + } + + val formatStr = "%.${precision}f" + + return String.format(formatStr, size) + unit.name + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/JwtUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/JwtUtil.kt new file mode 100644 index 0000000..8622a6b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/JwtUtil.kt @@ -0,0 +1,80 @@ +package top.fatweb.oxygen.api.util + +import com.auth0.jwt.JWT +import com.auth0.jwt.algorithms.Algorithm +import com.auth0.jwt.interfaces.DecodedJWT +import top.fatweb.oxygen.api.properties.SecurityProperties +import java.util.* +import java.util.concurrent.TimeUnit +import javax.crypto.spec.SecretKeySpec + +/** + * Jwt util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object JwtUtil { + private fun getUUID() = UUID.randomUUID().toString().replace("-", "") + + /** + * Generate encrypted secret key + * + * @return Secret key + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + private fun generalKey(): SecretKeySpec { + val encodeKey = Base64.getEncoder().encode(SecurityProperties.jwtKey.toByteArray()) + return SecretKeySpec(encodeKey, 0, encodeKey.size, "AES") + } + + private fun algorithm(): Algorithm = Algorithm.HMAC256(generalKey().toString()) + + /** + * Create token + * + * @param subject Data stored in token (json format) + * @param ttl TTL of token + * @param timeUnit TTL unit of token + * @param uuid UUID + * @return JWT token + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TimeUnit + */ + fun createJwt( + subject: String, + ttl: Long = SecurityProperties.jwtTtl, + timeUnit: TimeUnit = SecurityProperties.jwtTtlUnit, + uuid: String = getUUID() + ): String? { + val nowMillis = System.currentTimeMillis() + val nowDate = Date(nowMillis) + val unitTtl = (ttl * when (timeUnit) { + TimeUnit.DAYS -> 24 * 60 * 60 * 1000 + TimeUnit.HOURS -> 60 * 60 * 1000 + TimeUnit.MINUTES -> 60 * 1000 + TimeUnit.SECONDS -> 1000 + TimeUnit.MILLISECONDS -> 1 + TimeUnit.NANOSECONDS -> 1 / 1000 + TimeUnit.MICROSECONDS -> 1 / 1000 / 1000 + }) + val expMillis = nowMillis + unitTtl + val expDate = Date(expMillis) + + return JWT.create().withJWTId(uuid).withSubject(subject).withIssuer(SecurityProperties.jwtIssuer) + .withIssuedAt(nowDate).withExpiresAt(expDate).sign(algorithm()) + } + + /** + * Parse JWT token + * + * @param jwt JWT token + * @return Parsed content + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see DecodedJWT + */ + fun parseJwt(jwt: String): DecodedJWT = JWT.require(algorithm()).build().verify(jwt) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/MailUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/MailUtil.kt new file mode 100644 index 0000000..d89d02c --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/MailUtil.kt @@ -0,0 +1,57 @@ +package top.fatweb.oxygen.api.util + +import org.springframework.mail.javamail.JavaMailSenderImpl +import org.springframework.mail.javamail.MimeMessageHelper +import top.fatweb.oxygen.api.exception.NoEmailConfigException +import top.fatweb.oxygen.api.settings.MailSecurityType +import top.fatweb.oxygen.api.settings.MailSettings +import top.fatweb.oxygen.api.settings.SettingsOperator +import java.util.* + +/** + * Mail util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object MailUtil { + private val mailSender: JavaMailSenderImpl = JavaMailSenderImpl() + + init { + init() + } + + fun init() { + mailSender.defaultEncoding = Charsets.UTF_8.name() + mailSender.protocol = "smtp" + mailSender.host = SettingsOperator.getMailValue(MailSettings::host) + SettingsOperator.getMailValue(MailSettings::port)?.let { mailSender.port = it } + mailSender.username = SettingsOperator.getMailValue(MailSettings::username) + mailSender.password = SettingsOperator.getMailValue(MailSettings::password) + + val properties = Properties() + when (SettingsOperator.getMailValue(MailSettings::securityType)) { + MailSecurityType.SSL_TLS -> properties.setProperty("mail.smtp.ssl.enable", "true") + MailSecurityType.START_TLS -> properties.setProperty("mail.smtp.starttls.enable", "true") + else -> {} + } + properties["mail.smtp.timeout"] = "10000" + mailSender.javaMailProperties = properties + } + + fun sendSimpleMail(subject: String, text: String, html: Boolean = false, vararg to: String) { + val fromName = SettingsOperator.getMailValue(MailSettings::fromName) ?: throw NoEmailConfigException("fromName") + val from = SettingsOperator.getMailValue(MailSettings::from) ?: throw NoEmailConfigException("from") + + val mimeMessage = mailSender.createMimeMessage() + val messageHelper = MimeMessageHelper(mimeMessage, true) + messageHelper.apply { + setSubject(subject) + setFrom(from, fromName) + setSentDate(Date()) + setTo(to) + setText(text, html) + } + mailSender.send(mimeMessage) + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/NumberUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/NumberUtil.kt new file mode 100644 index 0000000..48db483 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/NumberUtil.kt @@ -0,0 +1,35 @@ +package top.fatweb.oxygen.api.util + +/** + * Number util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object NumberUtil { + /** + * Get random int value + * + * @param start Lower limit of random int value + * @param end Upper limit of random int value + * @return Random int value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getRandomInt(start: Int = Int.MIN_VALUE, end: Int = Int.MAX_VALUE): Int { + return (start..end).random() + } + + /** + * Get random long value + * + * @param start Lower limit of random long value + * @param end Upper limit of random long value + * @return Random long value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getRandomLong(start: Long = Long.MIN_VALUE, end: Long = Long.MAX_VALUE): Long { + return (start..end).random() + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/PageUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/PageUtil.kt new file mode 100644 index 0000000..4de5005 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/PageUtil.kt @@ -0,0 +1,42 @@ +package top.fatweb.oxygen.api.util + +import com.baomidou.mybatisplus.core.metadata.OrderItem +import com.baomidou.mybatisplus.extension.plugins.pagination.Page +import top.fatweb.oxygen.api.param.PageSortParam + +/** + * Page util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object PageUtil { + /** + * Set sort parameters into page object + * + * @param pageSortParam Sort parameters + * @param page Page object + * @param defaultOrder Default order by + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Page + * @see PageSortParam + * @see OrderItem + */ + fun > setPageSort(pageSortParam: PageSortParam?, page: T, defaultOrder: OrderItem? = null) { + if (pageSortParam?.sortField != null || pageSortParam?.sortOrder != null) { + page.addOrder( + if (pageSortParam.sortOrder?.startsWith( + "desc", true + ) == true + ) OrderItem.desc(StrUtil.upperToUnderLetter(pageSortParam.sortField)) else OrderItem.asc( + StrUtil.upperToUnderLetter( + pageSortParam.sortField + ) + ) + ) + } else { + defaultOrder?.let { page.addOrder(defaultOrder) } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/RedisUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/RedisUtil.kt new file mode 100644 index 0000000..67dd99e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/RedisUtil.kt @@ -0,0 +1,230 @@ +package top.fatweb.oxygen.api.util + +import org.springframework.data.redis.core.BoundSetOperations +import org.springframework.data.redis.core.RedisTemplate +import org.springframework.stereotype.Component +import java.util.concurrent.TimeUnit + +/** + * Redis util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Suppress("UNCHECKED_CAST") +@Component +class RedisUtil(private val redisTemplate: RedisTemplate) { + /** + * Set valid time + * + * @param key Cache key + * @param timeout Timeout + * @param timeUnit Unit of timeout + * @return true=Success;false=Fail + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see TimeUnit + */ + fun setExpire(key: String, timeout: Long, timeUnit: TimeUnit = TimeUnit.SECONDS): Boolean = + redisTemplate.expire(key, timeout, timeUnit) + + /** + * Get valid time + * + * @param key Cache key + * @param timeUnit Unit of time + * @return Valid time + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getExpire(key: String, timeUnit: TimeUnit = TimeUnit.SECONDS): Long = redisTemplate.getExpire(key, timeUnit) + + /** + * Determine whether key exists + * + * @param key Cache key + * @return true=exist; false=not exist + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun hasKey(key: String): Boolean = redisTemplate.hasKey(key) + + /** + * Get list of cached key + * + * @param pattern Pattern of key + * @return List of key + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun keys(pattern: String): Set = redisTemplate.keys(pattern) + + /** + * Cache basic object + * + * @param key Cache key + * @param value Cache object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setObject(key: String, value: Any) = redisTemplate.opsForValue().set(key, value) + + /** + * Cache basic object + * + * @param key Cache key + * @param value Cache object + * @param timeout Timeout + * @param timeUnit Unit of timeout + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setObject(key: String, value: Any, timeout: Long, timeUnit: TimeUnit = TimeUnit.SECONDS) = + redisTemplate.opsForValue().set(key, value, timeout, timeUnit) + + + /** + * Get cached basic object + * + * @param key cache key + * @return Cached object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getObject(key: String): T? = redisTemplate.opsForValue().get(key) as? T + + /** + * Delete cached object + * + * @param key Cache key + * @return true=success; false=fail + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delObject(key: String): Boolean = redisTemplate.delete(key) + + /** + * Delete list of cached objects + * + * @param collection List of cache key + * @return Number of deleted objects + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delObject(collection: Collection): Long = redisTemplate.delete(collection) + + /** + * Cache list of objects + * + * @param key Cache key + * @param dataList List of objects + * @return Number of cached objects + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setList(key: String, dataList: List): Long? = redisTemplate.opsForList().rightPushAll(key, dataList) + + /** + * Get cached list of objects + * + * @param key Cache key + * @return List of cached objects + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getList(key: String): List? = redisTemplate.opsForList().range(key, 0, -1) as? List + + /** + * Cache set of objects + * + * @param key Cache key + * @param dataSet Set of objects + * @return Bound set operations + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setSet(key: String, dataSet: Set): BoundSetOperations { + val boundSetOps: BoundSetOperations = redisTemplate.boundSetOps(key) + for (data in dataSet) { + boundSetOps.add(data) + } + return boundSetOps + } + + /** + * Get set of cached objects + * + * @param key Cache key + * @return Set of cached object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getSet(key: String): Set? = redisTemplate.opsForSet().members(key) as? Set + + /** + * Cache map of objects + * + * @param key Cache key + * @param dataMap Map of objects + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setMap(key: String, dataMap: Map) = redisTemplate.opsForHash().putAll(key, dataMap) + + /** + * Get cached map of objects + * + * @param key Cache key + * @return Map of cached objects + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getMap(key: String): Map? = + redisTemplate.opsForHash().entries(key) as? Map + + /** + * Set value into cached map + * + * @param key Cache key + * @param hKey Map key + * @param value Value in map + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun setMapValue(key: String, hKey: String, value: Any) = + redisTemplate.opsForHash().put(key, hKey, value) + + /** + * Get value in cached map + * + * @param key Cache key + * @param hKey Map key + * @return Value in map + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getMapValue(key: String, hKey: String) = redisTemplate.opsForHash().get(key, hKey) + + /** + * Delete value in cached map + * + * @param key Cache key + * @param hKey Map key + * @return Number of deleted value + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun delMapValue(key: String, hKey: String): Long = redisTemplate.opsForHash().delete(key, hKey) + + /** + * Get multiple value in cached map + * + * @param key Cache key + * @param hKeys List of map key + * @return HashMap object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getMultiMapValue(key: String, hKeys: Collection): List = + redisTemplate.opsForHash().multiGet(key, hKeys) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/StrUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/StrUtil.kt new file mode 100644 index 0000000..c237ee0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/StrUtil.kt @@ -0,0 +1,80 @@ +package top.fatweb.oxygen.api.util + +import java.security.MessageDigest + +/** + * String util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object StrUtil { + + /** + * Convert CamelCase string to underscore-delimited string + * + * @param str CamelCase string + * @return Underscore-delimited string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun upperToUnderLetter(str: String?): String { + str ?: return "" + + return Regex("[A-Z]").replace(str) { matchResult -> "_${matchResult.value.lowercase()}" } + } + + /** + * Convert underscore-delimited string to CamelCase string + * + * @param str Underscore-delimited string + * @return CamelCase string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun underToUpperLetter(str: String?): String { + str ?: return "" + + return Regex("_[a-z]").replace(str) { matchResult -> matchResult.value.removePrefix("_").uppercase() } + } + + /** + * Get random password + * + * @param length Length of password + * @return Random password + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getRandomPassword(length: Int): String { + val characterSet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + val password = StringBuilder() + + for (i in 0 until length) { + password.append(characterSet.random()) + } + + return password.toString() + } + + /** + * Get MD5 of a string + * + * @param content Content + * @return MD5 of content + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun md5(content: String): String { + val hash = MessageDigest.getInstance("MD5").digest(content.toByteArray()) + val hex = StringBuilder(hash.size * 2) + for (b in hash) { + var str = Integer.toHexString(b.toInt()) + if (b < 0x10) { + str = "0$str" + } + hex.append(str.substring(str.length - 2)) + } + return hex.toString() + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/TOTPUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/TOTPUtil.kt new file mode 100644 index 0000000..93d5b63 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/TOTPUtil.kt @@ -0,0 +1,198 @@ +package top.fatweb.oxygen.api.util + +import com.google.zxing.BarcodeFormat +import com.google.zxing.EncodeHintType +import com.google.zxing.common.BitMatrix +import com.google.zxing.qrcode.QRCodeWriter +import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel +import org.apache.commons.codec.binary.Base32 +import java.net.URLEncoder +import java.nio.ByteBuffer +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec +import kotlin.experimental.and +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi +import kotlin.math.pow +import kotlin.random.Random +import kotlin.time.Duration.Companion.seconds + +/** + * TOTP util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object TOTPUtil { + private val TIME_PERIOD = 30.seconds + private val FLEXIBLE_TIME = 5.seconds + private const val DIGITS = 6 + + private fun computeCounterForMillis(millis: Long): Long = Math.floorDiv(millis, TIME_PERIOD.inWholeMilliseconds) + + private fun generateHash(secret: ByteArray, payload: ByteArray): ByteArray { + val secretKey = Base32().decode(secret) + val hmacSha1 = Mac.getInstance("HmacSHA1") + hmacSha1.init(SecretKeySpec(secretKey, "RAW")) + + return hmacSha1.doFinal(payload) + } + + private fun truncateHash(hash: ByteArray): ByteArray { + val offset = hash.last().and(0x0F).toInt() + val truncatedHash = ByteArray(4) + for (i in 0..3) { + truncatedHash[i] = hash[offset + i] + } + truncatedHash[0] = truncatedHash[0].and(0x7F) + + return truncatedHash + } + + private fun calculateCode(key: String, time: Long): String { + val timeCounter = computeCounterForMillis(time) + val payload = ByteBuffer.allocate(8).putLong(0, timeCounter).array() + val secretKey = key.toByteArray(Charsets.UTF_8) + val hash = generateHash(secretKey, payload) + val truncatedHash = truncateHash(hash) + val code = ByteBuffer.wrap(truncatedHash).int % 10.0.pow(DIGITS).toInt() + + return code.toString().padStart(DIGITS, '0') + } + + /** + * Generate TOTP code + * + * @param secretKey Secret key + * @return TOTP Code + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun generateCode(secretKey: String, time: Long = System.currentTimeMillis()): String = + calculateCode(secretKey, time) + + /** + * Validate TOTP code + * + * @param secretKey Secret key + * @param code TOTP code + * @return Result + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun validateCode(secretKey: String, code: String): Boolean { + var time = System.currentTimeMillis() + if (calculateCode(secretKey, time) == code) { + return true + } + time -= FLEXIBLE_TIME.inWholeMilliseconds + + return calculateCode(secretKey, time) == code + } + + private fun encodeUrl(str: String) = URLEncoder.encode(str, Charsets.UTF_8).replace("+", "%20") + + /** + * Generate secret key + * + * @param length Secret key length + * @return Secret key + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun generateSecretKey(length: Int = 16): String { + if (length < 3) { + throw IllegalArgumentException("Password length should be at least 3.") + } + + val lowercaseChars = "abcdefghijklmnopqrstuvwxyz" + val uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + val digits = "0123456789" + val allChars = lowercaseChars + uppercaseChars + digits + + val secretKey = StringBuilder() + secretKey.append( + lowercaseChars[Random.nextInt(lowercaseChars.length)], + uppercaseChars[Random.nextInt(uppercaseChars.length)], + digits[Random.nextInt(digits.length)] + ) + repeat(length - 3) { + secretKey.append(allChars[Random.nextInt(allChars.length)]) + } + + return secretKey.toString().toList().shuffled().joinToString("") + } + + /** + * Generate TOTP URL + * + * @param issuer Issuer + * @param username Username + * @param secretKey Secret key + * @return TOTP URL + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun generateUrl(issuer: String, username: String, secretKey: String): String = + "otpauth://totp/${encodeUrl(issuer)}:${encodeUrl(username)}?secret=${encodeUrl(secretKey)}" + + /** + * Generate TOTP QR code + * + * @param issuer Issuer + * @param username Username + * @param secretKey Secret key + * @return QR code + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see BitMatrix + */ + fun generateQRCode(issuer: String, username: String, secretKey: String): BitMatrix { + val hints = HashMap() + hints[EncodeHintType.CHARACTER_SET] = Charsets.UTF_8 + hints[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.H + hints[EncodeHintType.MARGIN] = 1 + + return QRCodeWriter().encode(generateUrl(issuer, username, secretKey), BarcodeFormat.QR_CODE, 200, 200, hints) + } + + /** + * Generate TOTP QR code SVG + * + * @param issuer Issuer + * @param username Username + * @param secretKey Secret key + * @return QR code as SVG string + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun generateQRCodeSVG(issuer: String, username: String, secretKey: String): String { + val qrCode = generateQRCode(issuer, username, secretKey) + val stringBuilder = StringBuilder("") + + for (y in 0 until qrCode.height) { + for (x in 0 until qrCode.width) { + if (qrCode.get(x, y)) { + stringBuilder.appendLine(" ") + } + } + } + stringBuilder.append("") + + return stringBuilder.toString() + } + + /** + * Generate TOTP QR code SVG as Base64 string + * + * @param issuer Issuer + * @param username Username + * @param secretKey Secret key + * @return TOTP QR code base64 + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @OptIn(ExperimentalEncodingApi::class) + fun generateQRCodeSVGBase64(issuer: String, username: String, secretKey: String) = + Base64.encode(generateQRCodeSVG(issuer, username, secretKey).toByteArray(Charsets.UTF_8)) +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/util/WebUtil.kt b/src/main/kotlin/top/fatweb/oxygen/api/util/WebUtil.kt new file mode 100644 index 0000000..103e536 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/util/WebUtil.kt @@ -0,0 +1,107 @@ +package top.fatweb.oxygen.api.util + +import jakarta.servlet.http.HttpServletRequest +import org.springframework.security.core.context.SecurityContextHolder +import top.fatweb.oxygen.api.entity.permission.LoginUser +import top.fatweb.oxygen.api.properties.SecurityProperties + +/** + * Web util + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +object WebUtil { + /** + * Get the user currently calling api + * + * @return LoginUser object + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LoginUser + */ + fun getLoginUser(): LoginUser? = + if (SecurityContextHolder.getContext().authentication.principal is String) null + else SecurityContextHolder.getContext().authentication.principal as LoginUser + + /** + * Get ID of the user currently calling api + * + * @return User ID + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getLoginUserId(): Long? = getLoginUser()?.user?.id + + /** + * Get username of the user currently calling api + * + * @return Username + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getLoginUsername(): String? = getLoginUser()?.user?.username + + /** + * Get token of the user currently calling api + * + * @return Token + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getToken(tokenWithPrefix: String): String = tokenWithPrefix.removePrefix(SecurityProperties.tokenPrefix) + + /** + * Get token of the user currently calling api + * + * @return Token + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + fun getToken(request: HttpServletRequest): String = getToken(request.getHeader(SecurityProperties.headerKey)) + + /** + * Offline user + * + * @param redisUtil RedisUtil object + * @param userIds List of user IDs + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RedisUtil + */ + fun offlineUser(redisUtil: RedisUtil, vararg userIds: Long) { + val keys = HashSet() + userIds.forEach { + keys.addAll(redisUtil.keys("${SecurityProperties.jwtIssuer}_login_${it}:*")) + } + + redisUtil.delObject(keys) + } + + /** + * Get real request IP + * + * @param request HttpServletRequest object + * @return IP address + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HttpServletRequest + */ + fun getRequestIp(request: HttpServletRequest): String { + var ip = request.getHeader("X-Real-IP") + if (!ip.isNullOrBlank() && !"unknown".equals(ip, true)) { + return ip + } + ip = request.getHeader("X-Forwarded-For") + return if (!ip.isNullOrBlank() && !"unknown".equals(ip, true)) { + val index = ip.indexOf(",") + if (index != -1) { + ip.substring(0, index) + } else { + ip + } + } else { + request.remoteAddr + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/PageVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/PageVo.kt new file mode 100644 index 0000000..3990b58 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/PageVo.kt @@ -0,0 +1,57 @@ +package top.fatweb.oxygen.api.vo + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Page value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "分页返回参数") +data class PageVo( + /** + * Total number of records + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总数量", example = "100") + val total: Long, + + /** + * Total number of pages + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总页码", example = "10") + val pages: Long, + + /** + * Size of page + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "分页大小", example = "10") + val size: Long, + + /** + * Current page number + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "当前页码", example = "2") + val current: Long, + + /** + * Records in current page + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "数据") + val records: List +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/api/v1/avatar/AvatarBase64Vo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/api/v1/avatar/AvatarBase64Vo.kt new file mode 100644 index 0000000..34bb7bd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/api/v1/avatar/AvatarBase64Vo.kt @@ -0,0 +1,21 @@ +package top.fatweb.oxygen.api.vo.api.v1.avatar + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Avatar base64 value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "头像 Base64 返回参数") +data class AvatarBase64Vo( + /** + * Base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "base64") + val base64: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/GroupWithRoleVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/GroupWithRoleVo.kt new file mode 100644 index 0000000..7d5c28b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/GroupWithRoleVo.kt @@ -0,0 +1,72 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.RoleVo +import java.time.LocalDateTime + +/** + * Group with role value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class GroupWithRoleVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组名", example = "Role") + val name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", example = "true") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * List of RoleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleVo + */ + @Schema(description = "角色列表") + val roles: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/LoginVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/LoginVo.kt new file mode 100644 index 0000000..b49a3fd --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/LoginVo.kt @@ -0,0 +1,62 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Login value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "登录返回参数") +data class LoginVo( + /** + * Token + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "Token", + example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8" + ) val token: String, + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "User ID", + example = "1709986058679975938" + ) + @JsonSerialize(using = ToStringSerializer::class) + val userId: Long?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema( + description = "上次登录时间", + example = "1900-01-01 00:00:00" + ) val lastLoginTime: LocalDateTime?, + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "上次登录 IP", + example = "10.0.0.1" + ) val lastLoginIp: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/PowerSetVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/PowerSetVo.kt new file mode 100644 index 0000000..b3be209 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/PowerSetVo.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.vo.permission + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.FuncVo +import top.fatweb.oxygen.api.vo.permission.base.MenuVo +import top.fatweb.oxygen.api.vo.permission.base.ModuleVo +import top.fatweb.oxygen.api.vo.permission.base.OperationVo + +/** + * Set of power value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "权限集合返回参数") +data class PowerSetVo( + /** + * List of ModuleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ModuleVo + */ + @Schema(description = "模块列表") + val moduleList: List?, + + /** + * List of MenuVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MenuVo + */ + @Schema(description = "菜单列表") + val menuList: List?, + + /** + * List of FuncVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see FuncVo + */ + @Schema(description = "功能列表") + val funcList: List?, + + /** + * List of OperationVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see OperationVo + */ + @Schema(description = "操作列表") + val operationList: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RegisterVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RegisterVo.kt new file mode 100644 index 0000000..b15ca14 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RegisterVo.kt @@ -0,0 +1,35 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Register value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "注册返回参数") +data class RegisterVo( + /** + * Token + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "Token", + example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8" + ) val token: String, + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "User ID", example = "1709986058679975938") + @JsonSerialize(using = ToStringSerializer::class) + val userId: Long? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RoleWithPowerVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RoleWithPowerVo.kt new file mode 100644 index 0000000..d86b0cb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/RoleWithPowerVo.kt @@ -0,0 +1,106 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.FuncVo +import top.fatweb.oxygen.api.vo.permission.base.MenuVo +import top.fatweb.oxygen.api.vo.permission.base.ModuleVo +import top.fatweb.oxygen.api.vo.permission.base.OperationVo +import java.time.LocalDateTime + +/** + * Role with power value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "角色返回参数") +data class RoleWithPowerVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色名", example = "Role") + val name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", example = "true") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * List of ModuleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ModuleVo + */ + @Schema(description = "模块列表") + val modules: List?, + + /** + * List of MenuVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MenuVo + */ + @Schema(description = "菜单列表") + val menus: List?, + + /** + * List of FuncVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see FuncVo + */ + @Schema(description = "功能列表") + val funcs: List?, + + /** + * List of OperationVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see OperationVo + */ + @Schema(description = "操作列表") + val operations: List? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TokenVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TokenVo.kt new file mode 100644 index 0000000..669ea6e --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TokenVo.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.vo.permission + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Token value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "Token 返回参数") +data class TokenVo( + /** + * Token + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema( + description = "Token", + example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTllYjFkYmVmZDQ0OWRkOThlOGNjNzZlNzZkMDgyNSIsInN1YiI6IjE3MDk5ODYwNTg2Nzk5NzU5MzgiLCJpc3MiOiJGYXRXZWIiLCJpYXQiOjE2OTY1MjgxMTcsImV4cCI6MTY5NjUzNTMxN30.U2ZsyrGk7NbsP-DJfdz9xgWSfect5r2iKQnlEsscAA8" + ) val token: String +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TwoFactorVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TwoFactorVo.kt new file mode 100644 index 0000000..d49d8b9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/TwoFactorVo.kt @@ -0,0 +1,21 @@ +package top.fatweb.oxygen.api.vo.permission + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Two-factor value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "双因素返回参数") +data class TwoFactorVo ( + /** + * QR code SVG as base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "二维码 SVG Base64") + val qrCodeSVGBase64: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithInfoVo.kt new file mode 100644 index 0000000..4ee5b99 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithInfoVo.kt @@ -0,0 +1,158 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.UserInfoVo +import java.time.LocalDateTime + +/** + * User with information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户信息返回参数") +data class UserWithInfoVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户名", example = "User") + val username: String?, + + /** + * Two-factor enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用双因素", example = "true") + val twoFactor: Boolean?, + + /** + * Verified + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "已验证", example = "true") + val verified: Boolean?, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否锁定", example = "false") + val locking: Boolean?, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否启用", example = "true") + val enable: Boolean?, + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录时间", example = "1900-01-01T00:00:00.000Z") + val currentLoginTime: LocalDateTime?, + + /** + * Current login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "当前登录 IP", example = "1.1.1.1") + val currentLoginIp: String?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "最后登录时间", example = "1900-01-01T00:00:00.000Z") + val lastLoginTime: LocalDateTime?, + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "最后登录 IP", example = "1.1.1.1") + val lastLoginIp: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * User information object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoVo + */ + @Schema(description = "用户资料") + val userInfo: UserInfoVo? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPasswordRoleInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPasswordRoleInfoVo.kt new file mode 100644 index 0000000..35bbbe1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPasswordRoleInfoVo.kt @@ -0,0 +1,189 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.GroupVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo +import top.fatweb.oxygen.api.vo.permission.base.UserInfoVo +import java.time.LocalDateTime + +/** + * User with password role information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户密码角色信息返回参数") +data class UserWithPasswordRoleInfoVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户名", example = "User") + val username: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码") + val password: String?, + + /** + * Two-factor enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用双因素", example = "true") + val twoFactor: Boolean?, + + /** + * Verify + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证信息") + val verify: String?, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否锁定", example = "false") + val locking: Boolean?, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否启用", example = "true") + val enable: Boolean?, + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录时间", example = "1900-01-01T00:00:00.000Z") + val currentLoginTime: LocalDateTime?, + + /** + * Current login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "当前登录 IP", example = "1.1.1.1") + val currentLoginIp: String?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "最后登录时间", example = "1900-01-01T00:00:00.000Z") + val lastLoginTime: LocalDateTime?, + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "最后登录 IP", example = "1.1.1.1") + val lastLoginIp: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * User information object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoVo + */ + @Schema(description = "用户资料") + val userInfo: UserInfoVo?, + + /** + * List of RoleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleVo + */ + @Schema(description = "角色列表") + val roles: List?, + + /** + * List of GroupVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupVo + */ + @Schema(description = "用户组列表") + val groups: List? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPowerInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPowerInfoVo.kt new file mode 100644 index 0000000..d5e99c9 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithPowerInfoVo.kt @@ -0,0 +1,195 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.* +import java.time.LocalDateTime + +/** + * User with power information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户权限信息返回参数") +data class UserWithPowerInfoVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户名", example = "User") + val username: String?, + + /** + * Two-factor enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用双因素验证", example = "true") + val twoFactor: Boolean?, + + /** + * Verified + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "已验证", example = "true") + val verified: Boolean?, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否锁定", example = "false") + val locking: Boolean?, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否启用", example = "true") + val enable: Boolean?, + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录时间", example = "1900-01-01T00:00:00.000Z") + val currentLoginTime: LocalDateTime?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录 IP", example = "1.1.1.1") + val currentLoginIp: String?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "最后登录时间", example = "1900-01-01T00:00:00.000Z") + val lastLoginTime: LocalDateTime?, + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "最后登录 IP", example = "1.1.1.1") + val lastLoginIp: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * User information object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoVo + */ + @Schema(description = "用户资料") + val userInfo: UserInfoVo?, + + /** + * List of ModuleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "模块列表") + val modules: List?, + + /** + * List of MenuVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "菜单列表") + val menus: List?, + + /** + * List of FuncVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "功能列表") + val funcs: List?, + + /** + * List of OperationVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作列表") + val operations: List? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithRoleInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithRoleInfoVo.kt new file mode 100644 index 0000000..dd63140 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/UserWithRoleInfoVo.kt @@ -0,0 +1,182 @@ +package top.fatweb.oxygen.api.vo.permission + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.permission.base.GroupVo +import top.fatweb.oxygen.api.vo.permission.base.RoleVo +import top.fatweb.oxygen.api.vo.permission.base.UserInfoVo +import java.time.LocalDateTime + +/** + * User with role information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户角色信息返回参数") +data class UserWithRoleInfoVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户名", example = "User") + val username: String?, + + /** + * Two-factor enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用双因素", example = "true") + val twoFactor: Boolean?, + + /** + * Verify + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证信息") + val verify: String?, + + /** + * Locking + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否锁定", example = "false") + val locking: Boolean?, + + /** + * Expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "过期时间", example = "1900-01-01T00:00:00.000Z") + val expiration: LocalDateTime?, + + /** + * Credentials expiration time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "认证过期时间", example = "1900-01-01T00:00:00.000Z") + val credentialsExpiration: LocalDateTime?, + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "是否启用", example = "true") + val enable: Boolean?, + + /** + * Current login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录时间", example = "1900-01-01T00:00:00.000Z") + val currentLoginTime: LocalDateTime?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "当前登录 IP", example = "1.1.1.1") + val currentLoginIp: String?, + + /** + * Last login time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "最后登录时间", example = "1900-01-01T00:00:00.000Z") + val lastLoginTime: LocalDateTime?, + + /** + * Last login IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "最后登录 IP", example = "1.1.1.1") + val lastLoginIp: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * User information object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoVo + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * User information object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserInfoVo + */ + @Schema(description = "用户资料") + val userInfo: UserInfoVo?, + + /** + * List of RoleVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see RoleVo + */ + @Schema(description = "角色列表") + val roles: List?, + + /** + * List of GroupVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see GroupVo + */ + @Schema(description = "用户组列表") + val groups: List? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/FuncVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/FuncVo.kt new file mode 100644 index 0000000..656723f --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/FuncVo.kt @@ -0,0 +1,47 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Function value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "功能返回参数") +data class FuncVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "功能名", example = "AddButton") + val name: String?, + + /** + * Parent ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "父 ID") + val parentId: Long?, + + /** + * Menu ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "菜单 ID") + val menuId: Long? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/GroupVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/GroupVo.kt new file mode 100644 index 0000000..2e3da98 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/GroupVo.kt @@ -0,0 +1,62 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Group value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户组返回参数") +data class GroupVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户组名", example = "Group") + val name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", example = "true") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/MenuVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/MenuVo.kt new file mode 100644 index 0000000..78b26bc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/MenuVo.kt @@ -0,0 +1,56 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Menu value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "菜单返回参数") +data class MenuVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "菜单名", example = "System") + val name: String?, + + /** + * URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "URL", example = "/system") + val url: String?, + + /** + * Parent ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "父 ID") + val parentId: Long?, + + /** + * Module ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "模块 ID") + val moduleId: Long? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/ModuleVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/ModuleVo.kt new file mode 100644 index 0000000..2bb4af0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/ModuleVo.kt @@ -0,0 +1,29 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Module value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "模块返回参数") +data class ModuleVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "模块名", example = "系统") + val name: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/OperationVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/OperationVo.kt new file mode 100644 index 0000000..cb28ebb --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/OperationVo.kt @@ -0,0 +1,47 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Operation value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "操作返回参数") +data class OperationVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作名", example = "Add User") + val name: String?, + + /** + * Code + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作编码", example = "system:user:add") + val code: String?, + + /** + * Function ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "功能 ID") + val funcId: Long? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/RoleVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/RoleVo.kt new file mode 100644 index 0000000..2bcd01d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/RoleVo.kt @@ -0,0 +1,62 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Role value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "角色返回参数") +data class RoleVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "角色名", example = "Role") + val name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用", example = "true") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/UserInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/UserInfoVo.kt new file mode 100644 index 0000000..a6edbb8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/permission/base/UserInfoVo.kt @@ -0,0 +1,81 @@ +package top.fatweb.oxygen.api.vo.permission.base + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * User information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户资料返回参数") +data class UserInfoVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * User ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户ID") + @JsonSerialize(using = ToStringSerializer::class) + val userId: Long?, + + /** + * Nickname + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "昵称", example = "User") + val nickname: String?, + + /** + * Avatar base64 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "头像") + val avatar: String?, + + /** + * Email + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "邮箱", example = "user@mail.com") + val email: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/ActiveInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/ActiveInfoVo.kt new file mode 100644 index 0000000..1394ced --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/ActiveInfoVo.kt @@ -0,0 +1,63 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.system.ActiveInfoVo.HistoryVo +import java.time.LocalDate + +/** + * Active information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "用户活跃信息返回参数") +data class ActiveInfoVo( + /** + * Register user number history + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "注册用户数量历史") + val registerHistory: List, + + /** + * Login user number history + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HistoryVo + */ + @Schema(description = "登录用户数量历史") + val loginHistory: List, + + /** + * Verify user number history + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证用户数量历史") + val verifyHistory: List +) { + data class HistoryVo( + /** + * Time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDate + */ + @Schema(description = "记录时间", example = "1900-01-01") + val time: LocalDate, + + /** + * Count + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "数量") + val count: Int + ) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/BaseSettingsVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/BaseSettingsVo.kt new file mode 100644 index 0000000..1d70fd0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/BaseSettingsVo.kt @@ -0,0 +1,48 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Base settings value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "基础设置返回参数") +data class BaseSettingsVo( + /** + * Application name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "应用名称") + val appName: String?, + + /** + * Application URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "应用 URL") + val appUrl: String?, + + /** + * Verify URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "验证邮箱 URL") + val verifyUrl: String?, + + /** + * Retrieve URL + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "找回密码 URL") + val retrieveUrl: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/CpuInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/CpuInfoVo.kt new file mode 100644 index 0000000..17e843a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/CpuInfoVo.kt @@ -0,0 +1,124 @@ +package top.fatweb.oxygen.api.vo.system + +import com.fasterxml.jackson.annotation.JsonInclude +import io.swagger.v3.oas.annotations.media.Schema + +/** + * CPU information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "CPU 信息返回参数") +@JsonInclude(JsonInclude.Include.NON_EMPTY) +data class CpuInfoVo( + /** + * Show the percentage of CPU utilization + * that occurred while executing at the user + * level (application). + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "user") + val user: Long, + + /** + * Show the percentage of CPU utilization + * that occurred while executing at the user + * level with nice priority. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "nice") + val nice: Long, + + /** + * Show the percentage of CPU utilization that + * occurred while executing at the system level + * (kernel). Note that this does not include time + * spent servicing hardware and software + * interrupts. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "system") + val system: Long, + + /** + * Show the percentage of time that the + * CPU or CPUs were idle and the system did + * not have an outstanding disk I/O request. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "idle") + val idle: Long, + + /** + * Show the percentage of time that the + * CPU or CPUs were idle during which the + * system had an outstanding disk I/O + * request. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "iowait") + val iowait: Long, + + /** + * Show the percentage of time spent by + * the CPU or CPUs to service hardware + * interrupts. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "irq") + val irq: Long, + + /** + * Show the percentage of time spent by + * the CPU or CPUs to service software + * interrupts. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "softirq") + val softirq: Long, + + /** + * Show the percentage of time spent in + * involuntary wait by the virtual CPU or CPUs + * while the hypervisor was servicing another + * virtual processor. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "steal") + val steal: Long, + + /** + * total = user + nice + system + idle + iowait + irq + softirq + steal + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "total") + val total: Long, + + /** + * List of CPU processors information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "处理器列表") + val processors: List? = null +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/FileStoreInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/FileStoreInfoVo.kt new file mode 100644 index 0000000..e98b4c1 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/FileStoreInfoVo.kt @@ -0,0 +1,43 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * File storage information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "文件存储信息返回参数") +data class FileStoreInfoVo( + /** + * Mount point of the File System. The + * directory users will normally use to + * interface with the file store. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "挂载点") + val mount: String, + + /** + * Total space/capacity of the drive. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总容量") + val total: Long, + + /** + * Free space on the drive. This space is + * unallocated but may require elevated + * permissions to write. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "空闲容量") + val free: Long +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/HardwareInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/HardwareInfoVo.kt new file mode 100644 index 0000000..8f1e6a8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/HardwareInfoVo.kt @@ -0,0 +1,104 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Hardware information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "硬件信息返回参数") +data class HardwareInfoVo( + /** + * Name of CPU + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "CPU") + val cpu: String, + + /** + * Arch of CPU + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "架构") + val arch: String, + + /** + * Is CPU 64bit + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否为64位") + val is64Bit: Boolean, + + /** + * Number of packages/sockets in the system. A + * single package may contain multiple cores. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "物理 CPU") + val cpuPhysicalPackageCount: Int, + + /** + * Number of physical CPUs/cores available for + * processing. + * + * On some operating systems with variable numbers + * of physical processors available to the OS, may + * return a max value. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "物理核心") + val cpuPhysicalProcessorCount: Int, + + /** + * Number of logical CPUs available for processing. + * This value may be higher than physical CPUs if + * hyperthreading is enabled. + * + * On some operating systems with variable numbers + * of logical processors, may return a max value. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "逻辑核心") + val cpuLogicalProcessorCount: Int, + + /** + * Processor's microarchitecture, if known. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "微架构") + val microarchitecture: String, + + /** + * Memory information overview + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "内存") + val memories: String, + + /** + * Disk information overview + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "存储") + val disks: String +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/MailSettingsVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/MailSettingsVo.kt new file mode 100644 index 0000000..cf14853 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/MailSettingsVo.kt @@ -0,0 +1,76 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.settings.MailSecurityType + +/** + * Mail settings value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "邮件设置返回参数") +data class MailSettingsVo( + /** + * Host + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "SMTP 服务器") + val host: String?, + + /** + * Port + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "端口") + val port: Int?, + + /** + * Security type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "安全类型") + val securityType: MailSecurityType?, + + /** + * Username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户名") + val username: String?, + + /** + * Password + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密码") + val password: String?, + + /** + * Sender + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "发送者") + val from: String?, + + /** + * Sender name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "发送者名称") + val fromName: String? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/OnlineInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/OnlineInfoVo.kt new file mode 100644 index 0000000..4e76b70 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/OnlineInfoVo.kt @@ -0,0 +1,54 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.vo.system.OnlineInfoVo.HistoryVo +import java.time.LocalDateTime + +/** + * Online information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "在线信息返回参数") +data class OnlineInfoVo( + /** + * Number of user currently online + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "当前在线用户数量") + val current: Long, + + /** + * Online number history + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see HistoryVo + */ + @Schema(description = "历史记录") + val history: List +) { + data class HistoryVo( + /** + * Time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "记录时间") + val time: LocalDateTime, + + /** + * Record + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "记录") + val record: String + ) +} diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SensitiveWordVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SensitiveWordVo.kt new file mode 100644 index 0000000..5b547fc --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SensitiveWordVo.kt @@ -0,0 +1,52 @@ +package top.fatweb.oxygen.api.vo.system + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.entity.system.SensitiveWord + +/** + * Sensitive word settings value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(defaultValue = "敏感词设置返回参数") +data class SensitiveWordVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Word + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "词") + val word: String?, + + /** + * Use for + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see SensitiveWord.Use + */ + @Schema(description = "用于") + val useFor: Set?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用") + val enable: Boolean? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SettingsVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SettingsVo.kt new file mode 100644 index 0000000..7555b6b --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SettingsVo.kt @@ -0,0 +1,23 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * System settings value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + +@Schema(description = "系统设置返回参数") +data class SettingsVo( + /** + * MailSettingVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see MailSettingsVo + */ + @Schema(description = "邮件设置") + val mail: MailSettingsVo? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SoftwareInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SoftwareInfoVo.kt new file mode 100644 index 0000000..6a96097 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SoftwareInfoVo.kt @@ -0,0 +1,139 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Software information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "软甲信息返回参数") +data class SoftwareInfoVo( + /** + * Operating system + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作系统") + val os: String, + + /** + * Bitness (32 or 64) of the operating system. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "位数") + val bitness: Int, + + /** + * Version of Java + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 版本") + val javaVersion: String, + + /** + * Version date of Java + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 版本日期") + val javaVersionDate: String, + + /** + * Vendor of Java + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 供应商") + val javaVendor: String, + + /** + * Name of Java runtime + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 运行时") + val javaRuntime: String, + + /** + * Version of Java runtime + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 运行时版本") + val javaRuntimeVersion: String, + + /** + * Name of Java virtual machine + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机") + val jvm: String, + + /** + * Version of Java virtual machine + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机版本") + val jvmVersion: String, + + /** + * Version of Java Virtual machine + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机信息") + val jvmInfo: String, + + /** + * Vendor of Java Virtual machine + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机供应商") + val jvmVendor: String, + + /** + * Version of Java class + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 字节文件版本") + val javaClassVersion: String, + + /** + * Boot time of operating system + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作系统启动时间") + val osBootTime: LocalDateTime, + + /** + * Startup time of server + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "服务器启动时间") + val serverStartupTime: LocalDateTime +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/StorageInfoVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/StorageInfoVo.kt new file mode 100644 index 0000000..2a467a0 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/StorageInfoVo.kt @@ -0,0 +1,117 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Storage information value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "存储信息返回参数") +data class StorageInfoVo( + /** + * The amount of actual physical memory. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总内存容量") + val memoryTotal: Long, + + /** + * The amount of physical memory currently + * available。 + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "空闲内存容量") + val memoryFree: Long, + + /** + * The maximum memory that can be committed by the + * system without extending the paging file(s). Also called the + * Commit Limit. If the paging/swap file can be extended, this + * is a soft limit. This is generally equal to the sum of the sizes + * of physical memory and paging/swap file(s). + * + * On Linux, represents the total amount of memory currently + * available to be allocated on the system based on the + * overcommit ratio, identified as CommitLimit. This may be + * higher or lower than the total size of physical and swap + * memory depending on system configuration. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总虚拟内存容量") + val virtualMemoryMax: Long, + + /** + * The memory currently committed by the system, in + * bytes. Also called the Commit Total. This is generally + * equal to the sum of the bytes used of physical + * memory and paging/swap file(s). + * + * On Windows, committing pages changes this value + * immediately; however, the physical memory is not + * charged until the pages are accessed, so this + * value may exceed the sum of used physical and + * paging/swap file memory. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "已用虚拟内存容量") + val virtualMemoryInUse: Long, + + /** + * The current size of the paging/swap + * file(s). If the paging/swap file can be + * extended, this is a soft limit. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "总交换区容量") + val swapTotal: Long, + + /** + * The current memory committed to the + * paging/swap file(s). + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "可用交换区容量") + val swapUsed: Long, + + /** + * Total amount of memory in the Java virtual machine. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机总内存容量") + val jvmTotal: Long, + + /** + * Amount of free memory in the Java Virtual Machine. + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "Java 虚拟机空闲内存容量") + val jvmFree: Long, + + /** + * List of FileStoreInfoVo object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see FileStoreInfoVo + */ + @Schema(description = "文件存储信息列表") + val fileStores: List +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SysLogVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SysLogVo.kt new file mode 100644 index 0000000..4c44028 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/SysLogVo.kt @@ -0,0 +1,164 @@ +package top.fatweb.oxygen.api.vo.system + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.entity.system.SysLog +import java.time.LocalDateTime + +/** + * System log value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "获取系统日志返回参数") +data class SysLogVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Log type + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "日志类型") + val logType: SysLog.LogType?, + + /** + * Operate user ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作用户 ID") + @JsonSerialize(using = ToStringSerializer::class) + val operateUserId: Long?, + + /** + * Operate time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "操作时间") + val operateTime: LocalDateTime?, + + /** + * Request URI + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "请求 Uri") + val requestUri: String?, + + /** + * Request Method + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "请求方式") + val requestMethod: String?, + + /** + * Request parameters + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "请求参数") + val requestParams: String?, + + /** + * Request IP + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "请求 IP") + val requestIp: String?, + + /** + * Request server address + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "请求服务器地址") + val requestServerAddress: String?, + + /** + * Is exception + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "是否异常") + val exception: Boolean?, + + /** + * Exception information + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "异常信息") + val exceptionInfo: String?, + + /** + * Start time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "开始时间") + val startTime: LocalDateTime?, + + /** + * End time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "结束时间") + val endTime: LocalDateTime?, + + /** + * Execute time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "执行时间") + val executeTime: Long?, + + /** + * User agent + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "用户代理") + val userAgent: String?, + + /** + * Operate username + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "操作用户名") + val operateUsername: String? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/system/TwoFactorSettingsVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/TwoFactorSettingsVo.kt new file mode 100644 index 0000000..5e27d9d --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/system/TwoFactorSettingsVo.kt @@ -0,0 +1,30 @@ +package top.fatweb.oxygen.api.vo.system + +import io.swagger.v3.oas.annotations.media.Schema + +/** + * Two-factor settings value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "双因素设置返回参数") +data class TwoFactorSettingsVo( + /** + * Issuer + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "发布者") + val issuer: String?, + + /** + * Length of secret key + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "密钥长度") + val secretKeyLength: Int? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolBaseVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolBaseVo.kt new file mode 100644 index 0000000..eee7000 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolBaseVo.kt @@ -0,0 +1,91 @@ +package top.fatweb.oxygen.api.vo.tool + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.entity.tool.Platform +import java.time.LocalDateTime + +/** + * Tool base value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "工具基板返回参数") +data class ToolBaseVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "名称") + val name: String?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "源码") + val source: ToolDataVo?, + + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "产物") + val dist: ToolDataVo?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "平台") + val platform: Platform?, + + /** + * Compiled + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "已编译") + val compiled: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolCategoryVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolCategoryVo.kt new file mode 100644 index 0000000..3be4db8 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolCategoryVo.kt @@ -0,0 +1,62 @@ +package top.fatweb.oxygen.api.vo.tool + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Tool base value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "工具类别返回参数") +data class ToolCategoryVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "名称") + val name: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolDataVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolDataVo.kt new file mode 100644 index 0000000..309dbf4 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolDataVo.kt @@ -0,0 +1,53 @@ +package top.fatweb.oxygen.api.vo.tool + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import java.time.LocalDateTime + +/** + * Tool data value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "工具数据返回参数") +data class ToolDataVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Data + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "数据") + val data: String?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolTemplateVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolTemplateVo.kt new file mode 100644 index 0000000..e5d8e4a --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolTemplateVo.kt @@ -0,0 +1,100 @@ +package top.fatweb.oxygen.api.vo.tool + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.entity.tool.Platform +import java.time.LocalDateTime + +/** + * Tool template value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +@Schema(description = "工具模板返回参数") +data class ToolTemplateVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "名称") + val name: String?, + + /** + * Base + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "基板") + val base: ToolBaseVo?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "源码") + val source: ToolDataVo?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "平台") + val platform: Platform?, + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "入口文件") + val entryPoint: String?, + + /** + * Enable + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "启用") + val enable: Boolean?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime? +) \ No newline at end of file diff --git a/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolVo.kt b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolVo.kt new file mode 100644 index 0000000..4515dc2 --- /dev/null +++ b/src/main/kotlin/top/fatweb/oxygen/api/vo/tool/ToolVo.kt @@ -0,0 +1,197 @@ +package top.fatweb.oxygen.api.vo.tool + +import com.fasterxml.jackson.databind.annotation.JsonSerialize +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer +import io.swagger.v3.oas.annotations.media.Schema +import top.fatweb.oxygen.api.entity.tool.Platform +import top.fatweb.oxygen.api.entity.tool.Tool +import top.fatweb.oxygen.api.vo.permission.UserWithInfoVo +import java.time.LocalDateTime + +/** + * Tool value object + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ +data class ToolVo( + /** + * ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + val id: Long?, + + /** + * Name + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "名称") + val name: String?, + + /** + * Tool ID + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "工具 ID") + val toolId: String?, + + /** + * Icon + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "图标") + val icon: String?, + + /** + * Platform + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Platform + */ + @Schema(description = "平台") + val platform: Platform?, + + /** + * Description + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "简介") + val description: String?, + + /** + * Base + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "基板") + val base: ToolBaseVo?, + + /** + * Author + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see UserWithInfoVo + */ + @Schema(description = "作者") + val author: UserWithInfoVo?, + + /** + * Version + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "版本") + val ver: String?, + + /** + * Keywords + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "关键字") + val keywords: List?, + + /** + * Categories + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolCategoryVo + */ + @Schema(description = "类别") + val categories: List?, + + /** + * Source + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolDataVo + */ + @Schema(description = "源码") + val source: ToolDataVo?, + + /** + * Dist + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see ToolDataVo + */ + @Schema(description = "产物") + val dist: ToolDataVo?, + + /** + * Entry point + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "入口文件") + val entryPoint: String?, + + /** + * Publish + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @JsonSerialize(using = ToStringSerializer::class) + @Schema(description = "发布") + val publish: Long?, + + /** + * Review + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see Tool.ReviewType + */ + @Schema(description = "审核") + val review: Tool.ReviewType?, + + /** + * Create time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "创建时间", example = "1900-01-01T00:00:00.000Z") + val createTime: LocalDateTime?, + + /** + * Update time + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + * @see LocalDateTime + */ + @Schema(description = "修改时间", example = "1900-01-01T00:00:00.000Z") + val updateTime: LocalDateTime?, + + /** + * Favorite + * + * @author FatttSnake, fatttsnake@gmail.com + * @since 1.0.0 + */ + @Schema(description = "收藏") + val favorite: Boolean? +) \ No newline at end of file diff --git a/src/main/resources/application-config-template.yml b/src/main/resources/application-config-template.yml new file mode 100644 index 0000000..6747dbc --- /dev/null +++ b/src/main/resources/application-config-template.yml @@ -0,0 +1,66 @@ +app: + app-name: Oxygen Toolbox # Application name + turnstile-secret-key: 1x0000000000000000000000000000000AA # Turnstile secret key + admin: +# username: admin # Username of administrator +# password: admin # Default password of administrator +# nickname: Administrator # Nickname of administrator +# email: admin@mail.com # Email of administrator + security: +# header-string: "Authorization" # The key of head to get token +# token-prefix: "Bearer " # Token prefix +# jwt-ttl: 2 # The life of token +# jwt-ttl-unit: hours # Unit of life of token [nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days] + jwt-key: $uuid$ # Key to generate token +# jwt-issuer: Oxygen # Token issuer +# redis-ttl: 20 # The life of token in redis +# redis-ttl-unit: minutes # Unit of life of token in redis [nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days] + +server: +# port: 8080 # Server port + +spring: + datasource: + dynamic: + datasource: + master: + url: jdbc:mysql://localhost # MySQL url + username: root # MySQL username + password: root # MySQL password + hikari: +# pool-name: HikariCP # Connection pool name +# minimum-idle: 5 # Minimum number of connection in pool +# idle-timeout: 600000 # The lifetime of connection when idle +# maximum-pool-size: 10 # Maximum number of connection in pool +# auto-commit: true # Auto commit +# max-lifetime: 1800000 # Maximum lifetime of connection +# connection-timeout: 30000 # Connection timeout +# connection-test-query: select 1 # Test query then connect + + + data: + redis: +# database: 0 # Redis database (default: 0) +# host: localhost # Redis host (default: localhost) +# port: 6379 # Redis port (default: 6379) +# password: # Password of redis +# connect-timeout: 3000 # Redis connect timeout +# lettuce: +# pool: +# min-idle: 0 +# max-idle: 8 +# max-active: 8 +# max-wait: -1ms + +logging: + level: + root: info # Logging level + file: + name: data/log/fat-api.log # Logging path and name + logback: + rollingpolicy: +# max-file-size: 10MB # Maximum log file size +# max-history: 7 # Maximum number of archive log files to keep + +knife4j: + production: true # Production environment mode will block doc \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8b13789..46ec570 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1 +1,47 @@ +app: + app-name: Oxygen Toolbox + version: @project.version@ + build-time: @build.timestamp@ +spring: + profiles: + active: config + datasource: + dynamic: + primary: master + strict: false + datasource: + master: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: com.mysql.cj.jdbc.Driver + sqlite: + type: com.zaxxer.hikari.HikariDataSource + driver-class-name: org.sqlite.JDBC + url: jdbc:sqlite:data/db/sqlite.db + + + jackson: + date-format: yyyy-MM-dd'T'HH:mm:ss.SSS'Z' + time-zone: GMT + flyway: + enabled: false + baseline-on-migrate: true + baseline-version: 0 + +mybatis-plus: + global-config: + db-config: + logic-delete-field: deleted + logic-not-delete-value: 0 + logic-delete-value: id + id-type: assign_id + type-aliases-package: top.fatweb.oxygen.api.entity + +logging: + level: + root: info + file: + name: data/log/fat-api.log + +knife4j: + enable: true \ No newline at end of file diff --git a/src/main/resources/db/migration/master/R__Basic_data.sql b/src/main/resources/db/migration/master/R__Basic_data.sql new file mode 100644 index 0000000..8de3b8b --- /dev/null +++ b/src/main/resources/db/migration/master/R__Basic_data.sql @@ -0,0 +1,195 @@ +insert into t_s_power_type (id, name) + values (1, 'module'), + (2, 'menu'), + (3, 'func'), + (4, 'operation') as new_value +on duplicate key update name = new_value.name; + +insert into t_s_power (id, type_id) + values (1000000, 1), + (1990000, 2), + (1010000, 2), + (1020000, 2), + (1030000, 2), + (1040000, 2), + (1510000, 2), + (1520000, 2), + (1530000, 2), + (1540000, 2), + (1010100, 3), + (1010200, 3), + (1010300, 3), + (1010400, 3), + (1020100, 3), + (1020200, 3), + (1020300, 3), + (1020400, 3), + (1030100, 3), + (1030200, 3), + (1030300, 3), + (1030400, 3), + (1040100, 3), + (1510100, 3), + (1520100, 3), + (1530100, 3), + (1530300, 3), + (1540100, 3), + (1540200, 3), + (1540300, 3), + (1540400, 3), + (1010101, 4), + (1010102, 4), + (1010103, 4), + (1010201, 4), + (1010301, 4), + (1010302, 4), + (1010401, 4), + (1010402, 4), + (1020101, 4), + (1020102, 4), + (1020103, 4), + (1020201, 4), + (1020301, 4), + (1020302, 4), + (1020401, 4), + (1020402, 4), + (1030101, 4), + (1030102, 4), + (1030103, 4), + (1030201, 4), + (1030301, 4), + (1030302, 4), + (1030401, 4), + (1030402, 4), + (1040103, 4), + (1510101, 4), + (1510102, 4), + (1510103, 4), + (1520101, 4), + (1530101, 4), + (1530102, 4), + (1530103, 4), + (1530104, 4), + (1530301, 4), + (1530302, 4), + (1530303, 4), + (1530304, 4), + (1540101, 4), + (1540102, 4), + (1540103, 4), + (1540104, 4), + (1540201, 4), + (1540202, 4), + (1540203, 4), + (1540204, 4), + (1540301, 4), + (1540302, 4), + (1540303, 4), + (1540304, 4), + (1540401, 4), + (1540402, 4), + (1540403, 4), + (1540404, 4) as new_value +on duplicate key update type_id = new_value.type_id; + +insert into t_s_module (id, name) + values (1000000, '系统') as new_value +on duplicate key update name = new_value.name; + +insert into t_s_menu (id, name, url, parent_id, module_id) + values (1990000, '系统管理', '^/system$', null, 1000000), + (1010000, '用户管理', '^/system/user$', 1990000, 1000000), + (1020000, '角色管理', '^/system/role$', 1990000, 1000000), + (1030000, '用户组管理', '^/system/group$', 1990000, 1000000), + (1040000, '权限管理', '^/system/power$', 1990000, 1000000), + (1510000, '系统概况', '^/system/statistics$', 1990000, 1000000), + (1520000, '日志管理', '^/system/log$', 1990000, 1000000), + (1530000, '系统设置', '^/system/settings$', 1990000, 1000000), + (1540000, '工具配置', '^/system/tools(/.*)?$', 1990000, 1000000) as new_value +on duplicate key update name =new_value.name, + url =new_value.url, + parent_id =new_value.parent_id; + +insert into t_s_func(id, name, menu_id, parent_id) + values (1010100, '查询', 1010000, null), + (1010200, '增加', 1010000, null), + (1010300, '修改', 1010000, null), + (1010400, '删除', 1010000, null), + (1020100, '查询', 1020000, null), + (1020200, '增加', 1020000, null), + (1020300, '修改', 1020000, null), + (1020400, '删除', 1020000, null), + (1030100, '查询', 1030000, null), + (1030200, '增加', 1030000, null), + (1030300, '修改', 1030000, null), + (1030400, '删除', 1030000, null), + (1040100, '查询', 1040000, null), + (1510100, '查询', 1510000, null), + (1520100, '查询', 1520000, null), + (1530100, '查询', 1530000, null), + (1530300, '修改', 1530000, null), + (1540100, '查询', 1540000, null), + (1540200, '增加', 1540000, null), + (1540300, '修改', 1540000, null), + (1540400, '删除', 1540000, null) as new_value +on duplicate key update name = new_value.name, + menu_id = new_value.menu_id, + parent_id = new_value.parent_id; + +insert into t_s_operation(id, name, code, func_id) + values (1010101, '单个', 'system:user:query:one', 1010100), + (1010102, '全部', 'system:user:query:all', 1010100), + (1010103, '列表', 'system:user:query:list', 1010100), + (1010201, '单个', 'system:user:add:one', 1010200), + (1010301, '单个', 'system:user:modify:one', 1010300), + (1010302, '密码', 'system:user:modify:password', 1010300), + (1010401, '单个', 'system:user:delete:one', 1010400), + (1010402, '多个', 'system:user:delete:multiple', 1010400), + (1020101, '单个', 'system:role:query:one', 1020100), + (1020102, '全部', 'system:role:query:all', 1020100), + (1020103, '列表', 'system:role:query:list', 1020100), + (1020201, '单个', 'system:role:add:one', 1020200), + (1020301, '单个', 'system:role:modify:one', 1020300), + (1020302, '状态', 'system:role:modify:status', 1020300), + (1020401, '单个', 'system:role:delete:one', 1020400), + (1020402, '多个', 'system:role:delete:multiple', 1020400), + (1030101, '单个', 'system:group:query:one', 1030100), + (1030102, '全部', 'system:group:query:all', 1030100), + (1030103, '列表', 'system:group:query:list', 1030100), + (1030201, '单个', 'system:group:add:one', 1030200), + (1030301, '单个', 'system:group:modify:one', 1030300), + (1030302, '状态', 'system:group:modify:status', 1030300), + (1030401, '单个', 'system:group:delete:one', 1030400), + (1030402, '多个', 'system:group:delete:multiple', 1030400), + (1040103, '列表', 'system:power:query:list', 1040100), + (1510101, '使用情况', 'system:statistics:query:usage', 1510100), + (1510102, '基础信息', 'system:statistics:query:base', 1510100), + (1510103, '实时信息', 'system:statistics:query:real', 1510100), + (1520101, '全部', 'system:log:query:all', 1520100), + (1530101, '基础', 'system:settings:query:base', 1530100), + (1530102, '邮件', 'system:settings:query:mail', 1530100), + (1530103, '敏感词', 'system:settings:query:sensitive', 1530100), + (1530104, '双因素', 'system:settings:query:two-factor', 1530100), + (1530301, '基础', 'system:settings:modify:base', 1530300), + (1530302, '邮件', 'system:settings:modify:mail', 1530300), + (1530303, '敏感词', 'system:settings:modify:sensitive', 1530300), + (1530304, '双因素', 'system:settings:modify:two-factor', 1530300), + (1540101, '类别', 'system:tool:query:category', 1540100), + (1540102, '基板', 'system:tool:query:base', 1540100), + (1540103, '模板', 'system:tool:query:template', 1540100), + (1540104, '工具', 'system:tool:query:tool', 1540100), + (1540201, '类别', 'system:tool:add:category', 1540200), + (1540202, '基板', 'system:tool:add:base', 1540200), + (1540203, '模板', 'system:tool:add:template', 1540200), + (1540204, '工具', 'system:tool:add:tool', 1540200), + (1540301, '类别', 'system:tool:modify:category', 1540300), + (1540302, '基板', 'system:tool:modify:base', 1540300), + (1540303, '模板', 'system:tool:modify:template', 1540300), + (1540304, '工具', 'system:tool:modify:tool', 1540300), + (1540401, '类别', 'system:tool:delete:category', 1540400), + (1540402, '基板', 'system:tool:delete:base', 1540400), + (1540403, '模板', 'system:tool:delete:template', 1540400), + (1540404, '工具', 'system:tool:delete:tool', 1540400) as new_value +on duplicate key update name=new_value.name, + code=new_value.code, + func_id=new_value.func_id; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231019__Add_table_'t_s_user'.sql b/src/main/resources/db/migration/master/V1_0_0_231019__Add_table_'t_s_user'.sql new file mode 100644 index 0000000..490aa4a --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231019__Add_table_'t_s_user'.sql @@ -0,0 +1,26 @@ +drop table if exists t_s_user; + +create table if not exists t_s_user +( + id bigint not null primary key, + username varchar(20) not null comment '用户名', + password char(70) not null comment '密码', + two_factor varchar(40) null comment '双因素', + verify varchar(144) null comment '验证邮箱', + forget varchar(144) null comment '忘记密码', + locking int not null comment '锁定', + expiration datetime null comment '过期时间', + credentials_expiration datetime null comment '认证过期时间', + enable int not null comment '启用', + current_login_time datetime null comment '当前登录时间', + current_login_ip varchar(128) null comment '当前登录 IP', + last_login_time datetime null comment '上次登录时间', + last_login_ip varchar(128) null comment '上次登录 IP', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_s_user_unique_username unique (username, deleted), + constraint t_s_user_unique_verify unique (verify, deleted), + constraint t_s_user_unique_forget unique (forget, deleted) +) comment '系统-用户表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231023__Add_table_'t_s_power_type'.sql b/src/main/resources/db/migration/master/V1_0_0_231023__Add_table_'t_s_power_type'.sql new file mode 100644 index 0000000..8544453 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231023__Add_table_'t_s_power_type'.sql @@ -0,0 +1,7 @@ +drop table if exists t_s_power_type; + +create table if not exists t_s_power_type +( + id bigint not null primary key, + name varchar(50) not null comment '权限类型名' +) comment '系统-权限类型表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231024__Add_table_'t_s_power'.sql b/src/main/resources/db/migration/master/V1_0_0_231024__Add_table_'t_s_power'.sql new file mode 100644 index 0000000..1407a9b --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231024__Add_table_'t_s_power'.sql @@ -0,0 +1,7 @@ +drop table if exists t_s_power; + +create table if not exists t_s_power +( + id bigint not null primary key, + type_id int not null comment '权限类型' +) comment '系统-权限表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231025__Add_table_'t_s_menu'.sql b/src/main/resources/db/migration/master/V1_0_0_231025__Add_table_'t_s_menu'.sql new file mode 100644 index 0000000..6b8de95 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231025__Add_table_'t_s_menu'.sql @@ -0,0 +1,10 @@ +drop table if exists t_s_menu; + +create table if not exists t_s_menu +( + id bigint not null primary key, + name varchar(30) not null comment ' 菜单名', + url varchar(100) null comment 'URL', + parent_id bigint null comment '父ID', + module_id bigint not null comment '模块ID' +) comment '系统-菜单表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231026__Add_table_'t_s_func'.sql b/src/main/resources/db/migration/master/V1_0_0_231026__Add_table_'t_s_func'.sql new file mode 100644 index 0000000..0f5f33f --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231026__Add_table_'t_s_func'.sql @@ -0,0 +1,9 @@ +drop table if exists t_s_func; + +create table if not exists t_s_func +( + id bigint not null primary key, + name varchar(100) not null comment '功能名', + parent_id bigint null comment '父ID', + menu_id bigint not null comment '菜单ID' +) comment '系统-功能表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231027__Add_table_'t_s_operation'.sql b/src/main/resources/db/migration/master/V1_0_0_231027__Add_table_'t_s_operation'.sql new file mode 100644 index 0000000..a527025 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231027__Add_table_'t_s_operation'.sql @@ -0,0 +1,9 @@ +drop table if exists t_s_operation; + +create table if not exists t_s_operation +( + id bigint not null primary key, + name varchar(50) not null comment '操作名', + code varchar(50) null comment '操作编码', + func_id bigint not null comment '功能ID' +) comment '系统-操作表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231028__Add_table_'t_s_group'.sql b/src/main/resources/db/migration/master/V1_0_0_231028__Add_table_'t_s_group'.sql new file mode 100644 index 0000000..6168a2b --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231028__Add_table_'t_s_group'.sql @@ -0,0 +1,13 @@ +drop table if exists t_s_group; + +create table if not exists t_s_group +( + id bigint not null primary key, + name varchar(30) not null comment '用户组名', + enable int not null comment '启用', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_s_group_unique unique (name, deleted) +) comment '系统-用户组表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231029__Add_table_'t_r_user_group'.sql b/src/main/resources/db/migration/master/V1_0_0_231029__Add_table_'t_r_user_group'.sql new file mode 100644 index 0000000..969c3a7 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231029__Add_table_'t_r_user_group'.sql @@ -0,0 +1,10 @@ +drop table if exists t_r_user_group; + +create table if not exists t_r_user_group +( + id bigint not null primary key, + user_id bigint not null comment '用户', + group_id bigint not null comment '用户组', + deleted bigint not null default 0, + version int not null default 0 +) comment '中间表-系统-用户-用户组'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231030__Add_table_'t_s_role'.sql b/src/main/resources/db/migration/master/V1_0_0_231030__Add_table_'t_s_role'.sql new file mode 100644 index 0000000..d553aea --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231030__Add_table_'t_s_role'.sql @@ -0,0 +1,13 @@ +drop table if exists t_s_role; + +create table if not exists t_s_role +( + id bigint not null primary key, + name varchar(20) not null comment '角色名', + enable int not null comment '启用', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_s_role_unique unique (name, deleted) +) comment '系统-角色表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231031__Add_table_'t_r_role_group'.sql b/src/main/resources/db/migration/master/V1_0_0_231031__Add_table_'t_r_role_group'.sql new file mode 100644 index 0000000..ba976d0 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231031__Add_table_'t_r_role_group'.sql @@ -0,0 +1,10 @@ +drop table if exists t_r_role_group; + +create table if not exists t_r_role_group +( + id bigint not null primary key, + role_id bigint not null comment '角色', + group_id bigint not null comment '用户组', + deleted bigint not null default 0, + version int not null default 0 +) comment '中间表-系统-角色-用户组'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231101__Add_table_'t_r_user_role'.sql b/src/main/resources/db/migration/master/V1_0_0_231101__Add_table_'t_r_user_role'.sql new file mode 100644 index 0000000..8703912 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231101__Add_table_'t_r_user_role'.sql @@ -0,0 +1,10 @@ +drop table if exists t_r_user_role; + +create table if not exists t_r_user_role +( + id bigint not null primary key, + user_id bigint not null comment '用户', + role_id bigint not null comment '角色', + deleted bigint not null default 0, + version int not null default 0 +) comment '中间表-系统-用户-角色'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231102__Add_table_'t_r_power_role'.sql b/src/main/resources/db/migration/master/V1_0_0_231102__Add_table_'t_r_power_role'.sql new file mode 100644 index 0000000..81bc1cc --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231102__Add_table_'t_r_power_role'.sql @@ -0,0 +1,10 @@ +drop table if exists t_r_power_role; + +create table if not exists t_r_power_role +( + id bigint not null primary key, + power_id bigint not null comment '权限', + role_id bigint not null comment '角色', + deleted bigint not null default 0, + version int not null default 0 +) comment '中间表-系统-权限-角色'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231103__Add_table_'t_s_module'.sql b/src/main/resources/db/migration/master/V1_0_0_231103__Add_table_'t_s_module'.sql new file mode 100644 index 0000000..b859553 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231103__Add_table_'t_s_module'.sql @@ -0,0 +1,7 @@ +drop table if exists t_s_module; + +create table if not exists t_s_module +( + id bigint not null primary key, + name varchar(100) not null comment '模块名' +) comment '系统-模块表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_231104__Add_table_'t_s_user_info'.sql b/src/main/resources/db/migration/master/V1_0_0_231104__Add_table_'t_s_user_info'.sql new file mode 100644 index 0000000..b2599f3 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_231104__Add_table_'t_s_user_info'.sql @@ -0,0 +1,16 @@ +drop table if exists t_s_user_info; + +create table if not exists t_s_user_info +( + id bigint not null primary key, + user_id bigint not null comment '用户ID', + nickname varchar(50) not null comment '昵称', + avatar text null comment '头像', + email varchar(100) not null comment '邮箱', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_s_user_info_unique_user_id unique (user_id, deleted), + constraint t_s_user_info_unique_email unique (email, deleted) +) comment '系统-用户资料表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240103__Add_table_‘t_s_sensitive_word'.sql b/src/main/resources/db/migration/master/V1_0_0_240103__Add_table_‘t_s_sensitive_word'.sql new file mode 100644 index 0000000..d6c2830 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240103__Add_table_‘t_s_sensitive_word'.sql @@ -0,0 +1,12 @@ +drop table if exists t_s_sensitive_word; + +create table if not exists t_s_sensitive_word +( + id bigint not null primary key, + word varchar(400) not null comment '词', + use_for varchar(50) null comment '用于', + enable int not null default 1 comment '启用', + deleted bigint not null default 0, + version int not null default 0, + constraint t_s_sensitive_word_unique_word unique (word, deleted) +) comment '系统-敏感词表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240115__Add_table_'t_b_tool_main'.sql b/src/main/resources/db/migration/master/V1_0_0_240115__Add_table_'t_b_tool_main'.sql new file mode 100644 index 0000000..4f07c84 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240115__Add_table_'t_b_tool_main'.sql @@ -0,0 +1,26 @@ +drop table if exists t_b_tool_main; + +create table if not exists t_b_tool_main +( + id bigint not null primary key, + name varchar(50) not null comment '工具名', + tool_id varchar(50) not null comment '工具 ID', + icon text not null comment '图标', + platform varchar(20) not null comment '平台', + description varchar(500) null comment '简介', + base_id bigint not null comment '基板 ID', + author_id bigint not null comment '作者 ID', + ver varchar(20) not null comment '版本', + keywords varchar(500) not null comment '关键字', + source_id bigint not null comment '源码 ID', + dist_id bigint not null comment '产物 ID', + entry_point varchar(64) not null default 'main.tsx' comment '入口文件', + publish bigint not null default 0 comment '发布', + review varchar(10) not null default 'NONE' comment '审核', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_b_tool_main_unique_tool_id_platform_author_ver unique (tool_id, platform, author_id, ver, deleted), + constraint t_b_tool_main_unique_tool_id_platform_author_publish unique (tool_id, platform, author_id, publish, deleted) +) comment '工具-主表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240116__Add_table_'t_b_tool_category'.sql b/src/main/resources/db/migration/master/V1_0_0_240116__Add_table_'t_b_tool_category'.sql new file mode 100644 index 0000000..1b5ef6e --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240116__Add_table_'t_b_tool_category'.sql @@ -0,0 +1,13 @@ +drop table if exists t_b_tool_category; + +create table if not exists t_b_tool_category +( + id bigint not null primary key, + name varchar(50) not null comment '类别名', + enable int not null default 1 comment '启用', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_tool_category_name unique (name, deleted) +) comment '工具-类别表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240117__Add_table_'t_r_tool_main_category'.sql b/src/main/resources/db/migration/master/V1_0_0_240117__Add_table_'t_r_tool_main_category'.sql new file mode 100644 index 0000000..c711b56 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240117__Add_table_'t_r_tool_main_category'.sql @@ -0,0 +1,10 @@ +drop table if exists t_r_tool_main_category; + +create table if not exists t_r_tool_main_category +( + id bigint not null primary key, + tool_id bigint not null comment '工具', + category_id bigint not null comment '类别', + deleted bigint not null default 0, + version int not null default 0 +) comment '中间表-工具-主表-类别'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240118__Add_table_'t_b_tool_data'.sql b/src/main/resources/db/migration/master/V1_0_0_240118__Add_table_'t_b_tool_data'.sql new file mode 100644 index 0000000..f05e75a --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240118__Add_table_'t_b_tool_data'.sql @@ -0,0 +1,11 @@ +drop table if exists t_b_tool_data; + +create table if not exists t_b_tool_data +( + id bigint not null primary key, + data mediumtext not null comment '数据', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0 +) comment '工具-数据表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240119__Add_table_'t_b_tool_template'.sql b/src/main/resources/db/migration/master/V1_0_0_240119__Add_table_'t_b_tool_template'.sql new file mode 100644 index 0000000..3b4bd89 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240119__Add_table_'t_b_tool_template'.sql @@ -0,0 +1,17 @@ +drop table if exists t_b_tool_template; + +create table if not exists t_b_tool_template +( + id bigint not null primary key, + name varchar(40) not null comment '模板名', + base_id bigint not null comment '基板 ID', + source_id bigint not null comment '源码 ID', + platform varchar(20) not null comment '平台', + entry_point varchar(64) not null default 'main.tsx' comment '入口文件', + enable int not null default 1 comment '启用', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_b_tool_template_unique_name_platform unique (name, platform, deleted) +) comment '工具-模板表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240120__Add_table_'t_b_tool_base'.sql b/src/main/resources/db/migration/master/V1_0_0_240120__Add_table_'t_b_tool_base'.sql new file mode 100644 index 0000000..0956bf6 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240120__Add_table_'t_b_tool_base'.sql @@ -0,0 +1,16 @@ +drop table if exists t_b_tool_base; + +create table if not exists t_b_tool_base +( + id bigint not null primary key, + name varchar(20) not null comment '基板名', + source_id bigint not null comment '源码 ID', + dist_id bigint not null comment '产物 ID', + platform varchar(20) not null comment '平台', + compiled int not null default 0 comment '已编译', + create_time datetime not null default (utc_timestamp()) comment '创建时间', + update_time datetime not null default (utc_timestamp()) comment '修改时间', + deleted bigint not null default 0, + version int not null default 0, + constraint t_b_tool_base_unique_name_platform unique (name, platform, deleted) +) comment '工具-基板表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/master/V1_0_0_240426__Add_table_'t_b_tool_favorite'.sql b/src/main/resources/db/migration/master/V1_0_0_240426__Add_table_'t_b_tool_favorite'.sql new file mode 100644 index 0000000..6eded91 --- /dev/null +++ b/src/main/resources/db/migration/master/V1_0_0_240426__Add_table_'t_b_tool_favorite'.sql @@ -0,0 +1,12 @@ +drop table if exists t_b_tool_favorite; + +create table if not exists t_b_tool_favorite +( + id bigint not null primary key, + user_id bigint not null comment '用户 ID', + author_id bigint not null comment '作者 ID', + tool_id varchar(50) not null comment '工具 ID', + deleted bigint not null default 0, + version int not null default 0, + constraint t_b_tool_favorite_unique_user_id_username_tool_id_platform unique (user_id, author_id, tool_id, deleted) +) comment '工具-收藏表'; \ No newline at end of file diff --git a/src/main/resources/db/migration/sqlite/V1_0_0_231212__Add_table_'t_l_sys_log'.sql b/src/main/resources/db/migration/sqlite/V1_0_0_231212__Add_table_'t_l_sys_log'.sql new file mode 100644 index 0000000..ba3c20a --- /dev/null +++ b/src/main/resources/db/migration/sqlite/V1_0_0_231212__Add_table_'t_l_sys_log'.sql @@ -0,0 +1,20 @@ +drop table if exists t_l_sys_log; + +create table t_l_sys_log -- 本地-系统日志表 +( + id integer not null, + log_type text not null, -- 日志类型 + operate_user_id integer not null, -- 操作用户 + operate_time text not null default (strftime('%Y-%m-%d %H:%M:%f', 'now')), -- 操作时间 + request_uri text null default null, -- 请求 URI + request_method text null default null, -- 请求方式 + request_params text null, -- 请求参数 + request_ip text not null, -- 请求 IP + request_server_address text not null, -- 请求服务器地址 + exception integer not null default 0, -- 是否异常 + exception_info text null, -- 异常信息 + start_time text not null, -- 开始时间 + end_time text not null, -- 结束时间 + execute_time integer null default null, -- 执行时间 + user_agent text null default null -- 用户代理 +); \ No newline at end of file diff --git a/src/main/resources/db/migration/sqlite/V1_0_0_231213__Add_table_'t_l_event_log'.sql b/src/main/resources/db/migration/sqlite/V1_0_0_231213__Add_table_'t_l_event_log'.sql new file mode 100644 index 0000000..d729c85 --- /dev/null +++ b/src/main/resources/db/migration/sqlite/V1_0_0_231213__Add_table_'t_l_event_log'.sql @@ -0,0 +1,9 @@ +drop table if exists t_l_event_log; + +create table if not exists t_l_event_log -- 本地-事件日志表 +( + id integer not null primary key, + event text not null, -- 事件, + operate_user_id integer not null, -- 操作用户 + operate_time text not null default (strftime('%Y-%m-%d %H:%M:%f', 'now')) -- 操作时间 +); \ No newline at end of file diff --git a/src/main/resources/db/migration/sqlite/V1_0_0_231214__Add_table_'t_l_statistics_log'.sql b/src/main/resources/db/migration/sqlite/V1_0_0_231214__Add_table_'t_l_statistics_log'.sql new file mode 100644 index 0000000..8f8331a --- /dev/null +++ b/src/main/resources/db/migration/sqlite/V1_0_0_231214__Add_table_'t_l_statistics_log'.sql @@ -0,0 +1,9 @@ +drop table if exists t_l_statistics_log; + +create table if not exists t_l_statistics_log -- 本地-统计日志表 +( + id integer not null primary key, + key text not null, -- 记录键 + value text not null, -- 记录值 + record_time text not null default (strftime('%Y-%m-%d %H:%M:%f', 'now')) -- 记录时间 +); \ No newline at end of file diff --git a/src/main/resources/mapper/permission/FuncMapper.xml b/src/main/resources/mapper/permission/FuncMapper.xml new file mode 100644 index 0000000..ef0e20e --- /dev/null +++ b/src/main/resources/mapper/permission/FuncMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/GroupMapper.xml b/src/main/resources/mapper/permission/GroupMapper.xml new file mode 100644 index 0000000..7f0d940 --- /dev/null +++ b/src/main/resources/mapper/permission/GroupMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/MenuMapper.xml b/src/main/resources/mapper/permission/MenuMapper.xml new file mode 100644 index 0000000..5b9e6de --- /dev/null +++ b/src/main/resources/mapper/permission/MenuMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/ModuleMapper.xml b/src/main/resources/mapper/permission/ModuleMapper.xml new file mode 100644 index 0000000..1b344eb --- /dev/null +++ b/src/main/resources/mapper/permission/ModuleMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/mapper/permission/OperationMapper.xml b/src/main/resources/mapper/permission/OperationMapper.xml new file mode 100644 index 0000000..5f4bf6f --- /dev/null +++ b/src/main/resources/mapper/permission/OperationMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/PowerMapper.xml b/src/main/resources/mapper/permission/PowerMapper.xml new file mode 100644 index 0000000..bb5286d --- /dev/null +++ b/src/main/resources/mapper/permission/PowerMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/mapper/permission/PowerRoleMapper.xml b/src/main/resources/mapper/permission/PowerRoleMapper.xml new file mode 100644 index 0000000..7cc22a5 --- /dev/null +++ b/src/main/resources/mapper/permission/PowerRoleMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/permission/RoleGroupMapper.xml b/src/main/resources/mapper/permission/RoleGroupMapper.xml new file mode 100644 index 0000000..4125e6e --- /dev/null +++ b/src/main/resources/mapper/permission/RoleGroupMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/permission/RoleMapper.xml b/src/main/resources/mapper/permission/RoleMapper.xml new file mode 100644 index 0000000..80fc349 --- /dev/null +++ b/src/main/resources/mapper/permission/RoleMapper.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/UserGroupMapper.xml b/src/main/resources/mapper/permission/UserGroupMapper.xml new file mode 100644 index 0000000..49b30ef --- /dev/null +++ b/src/main/resources/mapper/permission/UserGroupMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/permission/UserInfoMapper.xml b/src/main/resources/mapper/permission/UserInfoMapper.xml new file mode 100644 index 0000000..de8ac7a --- /dev/null +++ b/src/main/resources/mapper/permission/UserInfoMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/UserMapper.xml b/src/main/resources/mapper/permission/UserMapper.xml new file mode 100644 index 0000000..3832ac7 --- /dev/null +++ b/src/main/resources/mapper/permission/UserMapper.xml @@ -0,0 +1,344 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/permission/UserRoleMapper.xml b/src/main/resources/mapper/permission/UserRoleMapper.xml new file mode 100644 index 0000000..5906666 --- /dev/null +++ b/src/main/resources/mapper/permission/UserRoleMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/resources/mapper/system/SysLogMapper.xml b/src/main/resources/mapper/system/SysLogMapper.xml new file mode 100644 index 0000000..6960994 --- /dev/null +++ b/src/main/resources/mapper/system/SysLogMapper.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/tool/EditMapper.xml b/src/main/resources/mapper/tool/EditMapper.xml new file mode 100644 index 0000000..b7ad444 --- /dev/null +++ b/src/main/resources/mapper/tool/EditMapper.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/tool/ManagementMapper.xml b/src/main/resources/mapper/tool/ManagementMapper.xml new file mode 100644 index 0000000..f2c0dd8 --- /dev/null +++ b/src/main/resources/mapper/tool/ManagementMapper.xml @@ -0,0 +1,270 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/tool/StoreMapper.xml b/src/main/resources/mapper/tool/StoreMapper.xml new file mode 100644 index 0000000..10d5963 --- /dev/null +++ b/src/main/resources/mapper/tool/StoreMapper.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/tool/ToolBaseMapper.xml b/src/main/resources/mapper/tool/ToolBaseMapper.xml new file mode 100644 index 0000000..8378225 --- /dev/null +++ b/src/main/resources/mapper/tool/ToolBaseMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/tool/ToolCategoryMapper.xml b/src/main/resources/mapper/tool/ToolCategoryMapper.xml new file mode 100644 index 0000000..5be8646 --- /dev/null +++ b/src/main/resources/mapper/tool/ToolCategoryMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/tool/ToolDataMapper.xml b/src/main/resources/mapper/tool/ToolDataMapper.xml new file mode 100644 index 0000000..cb72ea3 --- /dev/null +++ b/src/main/resources/mapper/tool/ToolDataMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/main/resources/mapper/tool/ToolFavoriteMapper.xml b/src/main/resources/mapper/tool/ToolFavoriteMapper.xml new file mode 100644 index 0000000..9c98dda --- /dev/null +++ b/src/main/resources/mapper/tool/ToolFavoriteMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/tool/ToolTemplateMapper.xml b/src/main/resources/mapper/tool/ToolTemplateMapper.xml new file mode 100644 index 0000000..f22c254 --- /dev/null +++ b/src/main/resources/mapper/tool/ToolTemplateMapper.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/templates/email-password-changed-cn.vm b/src/main/resources/templates/email-password-changed-cn.vm new file mode 100644 index 0000000..aba67ca --- /dev/null +++ b/src/main/resources/templates/email-password-changed-cn.vm @@ -0,0 +1,78 @@ + + + + + + + 您的密码已更改 + + + +
+
+
密 码 已 更 改
+
${username},您好:
+
您在 ${appName}(${appUrl}) 的密码已更改,操作 + IP 地址为 [${ipAddress}]。如果不是您自己的操作,请尽快检查您的账号! +
+
此邮件由系统自动发送,请勿回复!
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/email-retrieve-password-cn.vm b/src/main/resources/templates/email-retrieve-password-cn.vm new file mode 100644 index 0000000..d520710 --- /dev/null +++ b/src/main/resources/templates/email-retrieve-password-cn.vm @@ -0,0 +1,90 @@ + + + + + + + 找回您的密码 + + + +
+
+
找 回 密 码
+
${username},您好:
+
您正在找回 ${appName}(${appUrl}) 的密码,操作 + IP 地址为 [${ipAddress}],如果是您自己的操作,请在 两小时内 点击下面的按钮找回您的密码: +
+ +
如果以上按钮无法点击,请复制此链接到浏览器地址栏中访问:${retrieveUrl}
+
如果并非本人操作,请忽略该邮件
+
此邮件由系统自动发送,请勿回复!
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/email-verify-account-cn.vm b/src/main/resources/templates/email-verify-account-cn.vm new file mode 100644 index 0000000..ead9e3a --- /dev/null +++ b/src/main/resources/templates/email-verify-account-cn.vm @@ -0,0 +1,89 @@ + + + + + + + 验证您的账号 + + + +
+
+
账 号 激 活
+
${username},您好:
+
感谢注册 ${appName}(${appUrl}),在继续使用之前,我们需要确定您的电子邮箱地址的有效性,请在 + 两小时内 点击下面的按钮帮助我们验证: +
+ +
如果以上按钮无法点击,请复制此链接到浏览器地址栏中访问:${verifyUrl} +
+
此邮件由系统自动发送,请勿回复!
+
+
+ + \ No newline at end of file diff --git a/src/test/kotlin/top/fatweb/api/FatwebApiApplicationTests.kt b/src/test/kotlin/top/fatweb/api/FatwebApiApplicationTests.kt deleted file mode 100644 index 3c32b03..0000000 --- a/src/test/kotlin/top/fatweb/api/FatwebApiApplicationTests.kt +++ /dev/null @@ -1,13 +0,0 @@ -package top.fatweb.api - -import org.junit.jupiter.api.Test -import org.springframework.boot.test.context.SpringBootTest - -@SpringBootTest -class FatwebApiApplicationTests { - - @Test - fun contextLoads() { - } - -} diff --git a/src/test/kotlin/top/fatweb/oxygen/api/OxygenApiApplicationTests.kt b/src/test/kotlin/top/fatweb/oxygen/api/OxygenApiApplicationTests.kt new file mode 100644 index 0000000..660eae7 --- /dev/null +++ b/src/test/kotlin/top/fatweb/oxygen/api/OxygenApiApplicationTests.kt @@ -0,0 +1,54 @@ +package top.fatweb.oxygen.api + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.test.context.junit.jupiter.SpringExtension +import top.fatweb.oxygen.api.properties.SecurityProperties +import top.fatweb.oxygen.api.util.ByteUtil +import top.fatweb.oxygen.api.util.JwtUtil +import top.fatweb.oxygen.api.util.StrUtil + + +@ExtendWith(SpringExtension::class) +class OxygenApiApplicationTests { + private val logger: Logger = LoggerFactory.getLogger(this::class.java) + + @Test + fun removePrefixTest() { + assertEquals("12312", "Bearer 12312".removePrefix(SecurityProperties.tokenPrefix)) + } + + @Test + fun jwtTest() { + val jwt = JwtUtil.createJwt("User") + assertEquals("User", jwt?.let { JwtUtil.parseJwt(it).subject }) + } + + /* + @Test + fun generatePassword() { + val passwordEncoder = BCryptPasswordEncoder() + logger.info(passwordEncoder.encode("admin@dev")) + } + */ + + @Test + fun byteUtilTest() { + assertEquals("512B", ByteUtil.formatByteSize(512)) + assertEquals("512KiB", ByteUtil.formatByteSize(512 * 1024)) + assertEquals("1.5MiB", ByteUtil.formatByteSize(1 * 1024 * 1024 + 512 * 1024)) + } + + @Test + fun upperToUnderLetterTest() { + assertEquals("create_time", StrUtil.upperToUnderLetter("createTime")) + } + + @Test + fun underToUpperLetterTest() { + assertEquals("createTime", StrUtil.underToUpperLetter("create_time")) + } +}