diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..2959201
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+*.js linguist-language=java
+*.css linguist-language=java
+*.html linguist-language=java
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7934245
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,55 @@
+.DS_Store
+/*backup/
+#/.idea
+/*/target/
+/*/*.iml
+# Editor directories and files
+#*.idea
+*.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+
+target/
+!.mvn/wrapper/maven-wrapper.jar
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+
+### IntelliJ IDEA ###
+#.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+.idea
+.mvn
+*.idea
+/.idea
+node_modules
+target
+logs
+docs
+docker
+jars
diff --git a/.gitkeep b/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..6fc9fe4
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,17 @@
+## Contributing to alldata
+
+*Before opening a pull request*, review the
+[Contributing to AllData guide](https://github.com/alldatacenter/alldata/blob/master/CONTRIBUTING.md).
+It lists steps that are required before creating a PR. In particular, consider:
+
+- Is the change important and ready enough to ask the community to spend time reviewing?
+- Have you searched for existing, related JIRAs and pull requests?
+- Is this a new feature that can stand alone as a third party project?
+- Is the change being proposed clearly explained and motivated?
+
+When you contribute code, you affirm that the contribution is your original work and that you
+license the work to the project under the project's open source license. Whether or not you
+state this explicitly, by submitting any copyrighted material via pull request, email, or
+other means you agree to license the material under the project's open source license and
+warrant that you have the legal authority to do so.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0ad25db
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 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 Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are 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.
+
+ 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.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ 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 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 work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ 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 AGPL, see
+ .
diff --git a/LICENSING.md b/LICENSING.md
new file mode 100644
index 0000000..c0c78c0
--- /dev/null
+++ b/LICENSING.md
@@ -0,0 +1,9 @@
+The source code of alldata is covered by the following dual licenses:
+
+(1) AGPL 3.0: https://www.gnu.org/licenses/agpl-3.0.en.html
+
+You can choose AGPL to use alldata.
+
+for more details.
+
+All contributions to the alldata are subject to this LICENSING file.
diff --git a/README.md b/README.md
index e69de29..e0bc05e 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,336 @@
+# 可定义数据中台
+
+## [官方文档](https://alldata.readthedocs.io/) | [安装文档](https://github.com/alldatacenter/alldata/blob/master/install.md)
+
+## 一、AllData体验版
+
+> 体验版地址:Test账号只有数据质量,体验更多功能可选参加会员通道
+>
+> 成为会员:享受会员权益,详情查看Github主页文档
+>
+> 地址:http://122.51.43.143/dashboard
+>
+> 账号:test/123456
+
+## 二、 官方网站
+> 官方文档:https://alldata.readthedocs.io
+>
+> 部署教程:https://github.com/alldatacenter/alldata/blob/master/install.md
+>
+> 教程文档:https://github.com/alldatacenter/alldata/blob/master/dts_quickstart.md
+> 教程文档2: https://github.com/alldatacenter/alldata/blob/master/bi_quickstart.md
+
+## 三、会员通道-商业版【可选参加】
+> 【腾讯文档】2023-大数据中台AllData会员通道
+>
+> https://docs.qq.com/sheet/DVFd6WXJLUHJ3eEZ1
+
+## github
+
+[](https://starchart.cc/alldatacenter/alldata)
+
+
+
+
+
+
+## 体验版
+
+
+
+
+
+## 可定义数据中台
+
+
+
+
+## 首页
+
+
+
+
+
+
+
+
+
+## 数据集成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据质量
+
+
+
+
+
+
+
+## 数据标准
+
+
+
+
+
+
+
+
+
+
+
+
+## 元数据管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据资产
+
+
+
+
+
+
+
+
+## 数据市场
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据比对
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## BI报表
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 流程编排
+
+
+
+
+
+
+
+
+## 系统监控
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 运维管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## AllData AI Studio 社区版
+
+
+
+
+## AllData Studio 社区版
+
+
+
+
+## AllData社区商业计划图
+
+
+
+## AllData社区项目业务流程图
+
+
+
+## AllData社区项目树状图
+
+
+
+## 全站式AllData产品路线图
+
+
+
+
+
+## AllData社区开发规划
+
+
+
+### 数据中台建设方法论
+
+> 确定数据中台的目标和范围: 在开始构建数据中台之前, 需要明确数据中台的目标和范围, 例如数据中台的主要功能, 应用场景, 覆盖范围等
+>
+> 收集数据源: 根据定义的目标和范围, 收集相关的数据源这些数据源可以包括内部和外部的结构化和非结构化数据, 实时数据, 历史数据等
+>
+> 数据预处理: 对收集到的数据进行预处理, 包括数据清洗, 归一化, 去重, 脱敏等操作, 以确保数据的准确性, 一致性和安全性
+>
+> 数据建模和集成: 对预处理的数据进行建模和集成, 包括定义数据模型, 设计数据流程, 数据映射和转换等操作, 以确保数据的结构和语义一致性
+>
+> 数据存储和管理: 将集成的数据存储在数据仓库或数据湖中, 并实现数据的管理, 备份和恢复
+>
+> 数据治理和质量管理: 实现数据质量监控, 数据血缘追溯, 数据安全保障, 合规性检查等数据治理和质量管理功能, 以保证数据的高质量和可信性
+>
+> 数据分析和应用: 基于数据中台, 实现数据分析和应用, 如数据挖掘, 数据可视化, 机器学习等, 以提高数据的价值和应用效果
+>
+> 持续优化和创新: 数据中台的建设是一个持续迭代的过程, 需要不断进行优化和创新, 以适应不断变化的数据需求和业务场景
+>
+> 需要注意的是, 数据中台建设需要依赖于先进的技术和方法, 如云计算, 大数据, 人工智能, 数据湖等
+>
+> 同时, 数据中台建设还需要依赖于跨部门的协同和配合, 以确保数据的一致性和可用性
+
+
+## 数字化转型
+>
+> 数字化转型是指将传统企业在信息化, 网络化, 智能化, 数据化等技术的支撑下, 对业务, 组织, 文化, 价值创造, 利益分配等方面进行全面的革新和升级,
+>
+> 以适应市场, 技术, 用户等环境的变化数字化转型的目标是实现企业从传统生产经营方式向数字化经营模式的转变, 提高企业的效率, 创新能力, 市场竞争力和盈利能力
+>
+> 数字化转型方法论可以概括为以下几个方面:
+>
+> 确定数字化转型的战略目标和方向, 明确数字化转型的意义和价值, 为数字化转型的实施提供方向和支撑
+>
+> 分析业务过程, 识别业务痛点和机会, 确定数字化转型的重点领域和项目, 以提高效率, 创新能力和用户体验为导向
+>
+> 优化组织结构和流程, 建立数字化组织架构和工作流程, 激发组织创新和员工动力, 提高业务效率和创新能力
+>
+> 采用先进的信息技术和数据技术, 例如云计算, 大数据, 人工智能, 物联网等, 为数字化转型提供技术支持
+>
+> 建立数字化文化, 通过数字化营销, 数字化服务, 数字化协同等方式, 提升品牌价值, 用户满意度和市场影响力
+>
+> 实施数字化监管, 建立数字化安全, 合规和风险控制体系, 确保数字化转型的合法性, 合规性和可持续性
+>
+> 数字化转型是一个复杂的过程, 需要综合运用战略, 组织, 技术, 文化, 监管等多方面的手段和方法, 才能取得成功
+
+## Architecture
+
+
+
+
+
+
+## Community
+
+> 加入微信群: https://docs.qq.com/doc/DVHlkSEtvVXVCdEFo
diff --git a/bi_quickstart.md b/bi_quickstart.md
new file mode 100644
index 0000000..99477c6
--- /dev/null
+++ b/bi_quickstart.md
@@ -0,0 +1,27 @@
+#### (BI模块配置流程)
+
+##### 根据官方部署文档,部署项目https://github.com/alldatacenter/alldata/blob/master/install.md
+
+###### 部署可能会遇到的问题:
+
+1. 在修改完配置文件后应该在alldata根目录进行打包,而不是单独模块打包,这样有些模块,无法打包成功
+
+2. 切记认真查阅部署文档
+
+ ##### 部署成功之后,点击BI模块点击修改操作进入修改界面,alldata-metadata-service模块会报错
+
+ 
+
+
+
+
+ 该模块爆的应该是数据库的错误,其实主要是因为该项目提供的数据库只有studio,缺少了foodmart2和robot数据库
+
+ 1:我们只需要新建这两个数据库并且把数据导入即可以下为数据库连接,https://gitee.com/zrxjava/srt-data/tree/master/db可以自己下载导入,我会把数据库文件放在项目根目录
+
+ 2:在数据库中新建完这两个数据库之后,主要查看studio数据库中metadata_source表中的db_schema字段注意该字段的数据,端口和用户名,密码是否和foodmart2库和robot库相匹配,并且注意数据库的访问权限
+
+ ##### 数据库补充完毕,就可以愉快的使用bi功能了
+ 
+
+
diff --git a/document/.gitkeep b/document/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/document/.readthedocs.yaml b/document/.readthedocs.yaml
new file mode 100644
index 0000000..ceff617
--- /dev/null
+++ b/document/.readthedocs.yaml
@@ -0,0 +1,23 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the version of Python and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.7"
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+ builder: html
+ configuration: document/source/conf.py
+
+# We recommend specifying your dependencies to enable reproducible builds:
+# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+python:
+ install:
+ - requirements: document/source/requirements.txt
diff --git a/document/Makefile b/document/Makefile
new file mode 100644
index 0000000..d0c3cbf
--- /dev/null
+++ b/document/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = source
+BUILDDIR = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/document/README.md b/document/README.md
new file mode 100644
index 0000000..faae2c4
--- /dev/null
+++ b/document/README.md
@@ -0,0 +1,32 @@
+# Document For AllData
+
+## build document
+> 1 pip3 install -U Sphinx
+>
+> 2 install dependency
+>
+```markdown
+pip3 install sphinx-autobuild
+pip3 install sphinx_rtd_theme
+pip3 install --upgrade myst-parser
+pip3 install sphinx_markdown_tables
+```
+> 3 配置mac(mac单独配置)
+```markdown
+echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.tuna.tsinghua.edu.cn/homebrew-bottles' >> ~/.bash_profile
+source ~/.bash_profile
+```
+> 4 brew install sphinx-doc
+>
+> 5 echo 'export PATH="/opt/homebrew/opt/sphinx-doc/libexec/bin:$PATH"' >> ~/.zshrc
+>
+> 6 sphinx-build --version 查看version
+>
+> 7 python3.10 -m pip install --upgrade pip
+>
+> 8 pip3 install --upgrade myst-parser && pip3 install sphinxawesome_theme
+>
+> 9 make html
+>
+> 10 双击访问本地alldata/document/build/html/index.html
+>
diff --git a/document/make.bat b/document/make.bat
new file mode 100644
index 0000000..dc1312a
--- /dev/null
+++ b/document/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.https://www.sphinx-doc.org/
+ exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/document/source/README.md b/document/source/README.md
new file mode 100644
index 0000000..e0bc05e
--- /dev/null
+++ b/document/source/README.md
@@ -0,0 +1,336 @@
+# 可定义数据中台
+
+## [官方文档](https://alldata.readthedocs.io/) | [安装文档](https://github.com/alldatacenter/alldata/blob/master/install.md)
+
+## 一、AllData体验版
+
+> 体验版地址:Test账号只有数据质量,体验更多功能可选参加会员通道
+>
+> 成为会员:享受会员权益,详情查看Github主页文档
+>
+> 地址:http://122.51.43.143/dashboard
+>
+> 账号:test/123456
+
+## 二、 官方网站
+> 官方文档:https://alldata.readthedocs.io
+>
+> 部署教程:https://github.com/alldatacenter/alldata/blob/master/install.md
+>
+> 教程文档:https://github.com/alldatacenter/alldata/blob/master/dts_quickstart.md
+> 教程文档2: https://github.com/alldatacenter/alldata/blob/master/bi_quickstart.md
+
+## 三、会员通道-商业版【可选参加】
+> 【腾讯文档】2023-大数据中台AllData会员通道
+>
+> https://docs.qq.com/sheet/DVFd6WXJLUHJ3eEZ1
+
+## github
+
+[](https://starchart.cc/alldatacenter/alldata)
+
+
+
+
+
+
+## 体验版
+
+
+
+
+
+## 可定义数据中台
+
+
+
+
+## 首页
+
+
+
+
+
+
+
+
+
+## 数据集成
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据质量
+
+
+
+
+
+
+
+## 数据标准
+
+
+
+
+
+
+
+
+
+
+
+
+## 元数据管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据资产
+
+
+
+
+
+
+
+
+## 数据市场
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 数据比对
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## BI报表
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 流程编排
+
+
+
+
+
+
+
+
+## 系统监控
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 运维管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## AllData AI Studio 社区版
+
+
+
+
+## AllData Studio 社区版
+
+
+
+
+## AllData社区商业计划图
+
+
+
+## AllData社区项目业务流程图
+
+
+
+## AllData社区项目树状图
+
+
+
+## 全站式AllData产品路线图
+
+
+
+
+
+## AllData社区开发规划
+
+
+
+### 数据中台建设方法论
+
+> 确定数据中台的目标和范围: 在开始构建数据中台之前, 需要明确数据中台的目标和范围, 例如数据中台的主要功能, 应用场景, 覆盖范围等
+>
+> 收集数据源: 根据定义的目标和范围, 收集相关的数据源这些数据源可以包括内部和外部的结构化和非结构化数据, 实时数据, 历史数据等
+>
+> 数据预处理: 对收集到的数据进行预处理, 包括数据清洗, 归一化, 去重, 脱敏等操作, 以确保数据的准确性, 一致性和安全性
+>
+> 数据建模和集成: 对预处理的数据进行建模和集成, 包括定义数据模型, 设计数据流程, 数据映射和转换等操作, 以确保数据的结构和语义一致性
+>
+> 数据存储和管理: 将集成的数据存储在数据仓库或数据湖中, 并实现数据的管理, 备份和恢复
+>
+> 数据治理和质量管理: 实现数据质量监控, 数据血缘追溯, 数据安全保障, 合规性检查等数据治理和质量管理功能, 以保证数据的高质量和可信性
+>
+> 数据分析和应用: 基于数据中台, 实现数据分析和应用, 如数据挖掘, 数据可视化, 机器学习等, 以提高数据的价值和应用效果
+>
+> 持续优化和创新: 数据中台的建设是一个持续迭代的过程, 需要不断进行优化和创新, 以适应不断变化的数据需求和业务场景
+>
+> 需要注意的是, 数据中台建设需要依赖于先进的技术和方法, 如云计算, 大数据, 人工智能, 数据湖等
+>
+> 同时, 数据中台建设还需要依赖于跨部门的协同和配合, 以确保数据的一致性和可用性
+
+
+## 数字化转型
+>
+> 数字化转型是指将传统企业在信息化, 网络化, 智能化, 数据化等技术的支撑下, 对业务, 组织, 文化, 价值创造, 利益分配等方面进行全面的革新和升级,
+>
+> 以适应市场, 技术, 用户等环境的变化数字化转型的目标是实现企业从传统生产经营方式向数字化经营模式的转变, 提高企业的效率, 创新能力, 市场竞争力和盈利能力
+>
+> 数字化转型方法论可以概括为以下几个方面:
+>
+> 确定数字化转型的战略目标和方向, 明确数字化转型的意义和价值, 为数字化转型的实施提供方向和支撑
+>
+> 分析业务过程, 识别业务痛点和机会, 确定数字化转型的重点领域和项目, 以提高效率, 创新能力和用户体验为导向
+>
+> 优化组织结构和流程, 建立数字化组织架构和工作流程, 激发组织创新和员工动力, 提高业务效率和创新能力
+>
+> 采用先进的信息技术和数据技术, 例如云计算, 大数据, 人工智能, 物联网等, 为数字化转型提供技术支持
+>
+> 建立数字化文化, 通过数字化营销, 数字化服务, 数字化协同等方式, 提升品牌价值, 用户满意度和市场影响力
+>
+> 实施数字化监管, 建立数字化安全, 合规和风险控制体系, 确保数字化转型的合法性, 合规性和可持续性
+>
+> 数字化转型是一个复杂的过程, 需要综合运用战略, 组织, 技术, 文化, 监管等多方面的手段和方法, 才能取得成功
+
+## Architecture
+
+
+
+
+
+
+## Community
+
+> 加入微信群: https://docs.qq.com/doc/DVHlkSEtvVXVCdEFo
diff --git a/document/source/conf.py b/document/source/conf.py
new file mode 100644
index 0000000..f3602ae
--- /dev/null
+++ b/document/source/conf.py
@@ -0,0 +1,28 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'AllData'
+copyright = '2023, AllDataDC'
+author = 'AllDataDC'
+release = '0.4.x'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ['myst_parser']
+
+templates_path = ['_templates']
+exclude_patterns = []
+
+language = 'zh_CN'
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'sphinxawesome_theme'
+html_static_path = ['_static']
diff --git a/document/source/donate/README.md b/document/source/donate/README.md
new file mode 100644
index 0000000..a78eb28
--- /dev/null
+++ b/document/source/donate/README.md
@@ -0,0 +1,24 @@
+# 会员通道
+
+## 一、AllData体验版
+
+> 体验版地址:Test账号只有数据质量,体验更多功能可选参加会员通道
+>
+> 成为会员:享受会员权益,详情查看Github主页文档
+>
+> 地址:http://122.51.43.143/dashboard
+>
+> 账号:test/123456
+
+## 二、 官方网站
+> 官方文档:https://alldata.readthedocs.io
+>
+> 部署教程:https://github.com/alldatacenter/alldata/blob/master/install.md
+>
+> 教程文档:https://github.com/alldatacenter/alldata/blob/master/studio/modules/service-data-dts-parent/DTS_QuickStart.md
+
+## 三、会员通道-商业版【可选参加】
+> 【腾讯文档】2023-大数据中台AllData会员通道
+>
+> https://docs.qq.com/sheet/DVFd6WXJLUHJ3eEZ1
+
diff --git a/document/source/donate/index.rst b/document/source/donate/index.rst
new file mode 100644
index 0000000..a72ea9b
--- /dev/null
+++ b/document/source/donate/index.rst
@@ -0,0 +1,7 @@
+募捐活动
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ README
diff --git a/document/source/index.rst b/document/source/index.rst
new file mode 100644
index 0000000..c690bdc
--- /dev/null
+++ b/document/source/index.rst
@@ -0,0 +1,16 @@
+.. alldata documentation master file, created by
+ sphinx-quickstart on Wed Jan 11 16:02:11 2023.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+可定义数据中台
+===================================
+
+.. toctree::
+ :maxdepth: 1
+
+ README
+ quickstart/index
+ studio/index
+ wiki/index
+ donate/index
diff --git a/document/source/quickstart/index.rst b/document/source/quickstart/index.rst
new file mode 100644
index 0000000..c276c26
--- /dev/null
+++ b/document/source/quickstart/index.rst
@@ -0,0 +1,7 @@
+快速上手
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ ../README
diff --git a/document/source/requirements.txt b/document/source/requirements.txt
new file mode 100644
index 0000000..6e7c524
--- /dev/null
+++ b/document/source/requirements.txt
@@ -0,0 +1,6 @@
+sphinxawesome-theme==3.3.7
+urllib3==1.26.15
+sphinx==5.3.0
+sphinx_rtd_theme==1.1.1
+readthedocs-sphinx-search==0.1.1
+myst-parser==1.0.0
diff --git a/document/source/studio/index.rst b/document/source/studio/index.rst
new file mode 100644
index 0000000..fca86c7
--- /dev/null
+++ b/document/source/studio/index.rst
@@ -0,0 +1,7 @@
+数据中台
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ ../README
diff --git a/document/source/wiki/.gitkeep b/document/source/wiki/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/document/source/wiki/alldata/.gitkeep b/document/source/wiki/alldata/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/document/source/wiki/alldata/README.md b/document/source/wiki/alldata/README.md
new file mode 100644
index 0000000..10a203f
--- /dev/null
+++ b/document/source/wiki/alldata/README.md
@@ -0,0 +1,146 @@
+# 2023年度社区分享
+
+```markdown
+AllData开源社区 ✖️ Datavines ✖️ 跑象科技 Meetup
+主要介绍AllData产品的技术及其应用场景,
+同时我们会邀请业内同行做嘉宾分享,让参与者有机会与嘉宾和其他社区成员互动。
+后续我们会继续分享AllData数字化上游生态和下游支撑平台的专题分享。
+```
+
+## 01 分享嘉宾介绍
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 02 AllData分享
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 03 Datavines分享
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 04 Datart分享
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## 05 Apache StreamPark作者分享
+
+
+
+
+
+```markdown
+StreamPark作者王华杰给AllData社区带来了较多问题较多的分享
+
+问题一:如何建设一款社区&&商业成功的开源平台
+
+回答:从可用版本,到好用版本,到可规模化,真正解决客户场景的问题,让客户用在实际场景。
+
+问题二:如何用一句话定义AllData
+
+回答:一站式的数据中台,提供能力开放的数据中台。
+
+问题三:有没有哪一块是AllData的核心优势,能在客户选型阶段必定会拿出来对比竞争的核心功能?比较拔尖出类拔萃的功能
+
+回答:AllData社区目前的功能正在集成阶段,后续会出更多贯穿场景的闭环功能。
+```
+
+
+
+
diff --git a/document/source/wiki/alldata/index.rst b/document/source/wiki/alldata/index.rst
new file mode 100644
index 0000000..006f735
--- /dev/null
+++ b/document/source/wiki/alldata/index.rst
@@ -0,0 +1,7 @@
+alldata
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ README
diff --git a/document/source/wiki/dinky/.gitkeep b/document/source/wiki/dinky/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/document/source/wiki/dinky/README.md b/document/source/wiki/dinky/README.md
new file mode 100644
index 0000000..ae83b77
--- /dev/null
+++ b/document/source/wiki/dinky/README.md
@@ -0,0 +1,93 @@
+# AllData社区与Dinky社区分享
+
+### AllData社区发起人邀请Dinky的作者文末aiwenmo对AllData社区建设进行分享和宝贵建议
+### Dlink为Apache Flink而生,让Flink SQL尽享丝般顺滑,致力于实时计算平台的构建
+### Dinky项目:https://github.com/DataLinkDC/dlink
+### AllData项目:https://github.com/alldatacenter/alldata
+### 感谢社区成员@yg9538的会议纪要
+- **第一:如何激发社区对用户来参与到项目的建设,一起推动项目的发展?**
+- **第二:完成整个项目的基本功能路线,流程是如何的?**
+
+**我首先回答第二个问题。首先AllData用到的技术栈包括我们所设想的整个流程是非常全面丰富的。但然,凡事都是有利有弊的,技术栈的全面会导致各个技术功能点实现复杂度增高。**
+
+### 2.1 最小MVP
+
+**对于第一个问题,其实跟第二个问题是非常有关系的。当我们具备了一个可使用的一个MVP最小可行性产品时候,用户将产品用在测试或者生产实践将会给项目带来极大的益处**
+**最大的好处是用户将会主动参与到我们那个项目的推动中。比如说项目的在实践中进行的测试和提出的ISSUE作为项目经验必不可少,其次用户在实际生产中遇到的二开需求,对于我们整个项目提升也是有极大的帮助。**
+
+### 2.2 门户与KM知识库
+
+**当然就是除了有一个最小可行性产品门户和知识库也必不可少,门户可以对整个项目进行一个详细的介绍。比如我们要进行部署的话我们可以进行搜索,百度上也会提供许多文档。但是百度的文档质量参差不齐,非常影响用户的体验。**
+
+**我看到AllData有提到三个概念我是非常赞同的:**
+
+- **重设计**
+- **轻编码**
+- **中度测试**
+
+**对于社区来说,我们可以总结自己的经验形成文档放在社区中。**
+
+### 2.3 用户分类
+
+**然后第三点,因为项目前期用户是较少,在前期用户对项目的发展至关重要。这就不仅需我们仔细的聆听用户的需求,用户的反馈,还要积极的为用户来进行解答,一般用户会划分为两大类:**
+
+- **有完整的技术经验——较少数**
+- **无完整的技术经验——占多数**
+
+**第一种用户是自身具备一定的技术栈,一定的能力。**
+
+**第二种用户是作为项目经理或技术他引入该开源项目来作为解决方案的**
+
+**门户网站和知识库至关重要,它可以帮助客户进行部署。所以问题点就是如何教导用户会用我们的产品。**
+
+### 2.4 工作推动
+
+**然后,就是进行一个分工推这方面。如果作为一个领导者要去领导我们的协作者去完成一件事情的时候,首先要达到一个统一的共识才可以,然后如何如何来达到这个统一的共识呢**
+
+#### 2.4.1 获得认可
+
+**首先就得需要认可你的方案**,认可你的思路。
+
+#### 2.4.2 产品定位
+
+**就是说你不要让用户上生产的时候就把你所有的功能都要上去,而是可以用你几块核心功能就可以完成一个产品。**
+
+### 2.5 技术选型
+
+#### 2.5.1 大数据平台选型
+
+基于这些平台,你可以独立完整的跑起一个MVP。这样不仅仅能享受到社区福利也可以获得用户的认可
+
+#### 2.5.2 MLOPS平台选型
+
+
+#### 2.5.3 CI/CD
+
+**CI/CD那一块我认为就比较独立了。这些也是有现成的项目,然后我们要做的其实就是调研好现成的项目。然后把它集成进来。**
+
+### 2.6 需求场景
+
+**我个人对AllData的定位是一个把很多开源的项目平台统一管理、集成起来,提供一个能力开放的平台**
+
+#### 2.6.1 定义场景
+
+**一般离线开发都是需要开发需求。**
+
+### 2.7 其他要求
+
+#### 2.7.1 文档要求
+
+**第一,文档永远没有交流重要,文档的规范可以放松,但是交流是最主要的。**
+
+#### 2.7.2 设计逻辑
+
+**第二就是设计。先是高层级设计,再是具体细节设计。**
+
+**这具体如何实现呢?例如我们如何去把数据开发平台、调度平台、管理平台关联?首先就需要把他们打通,作为一个底层Base先把他们真正的打通起来。然后让他们可以再以流一个流程化的方式来跑起来。**
+
+**建议总结:**
+
+- **压缩技术栈**
+- **理念转变为团队思想**
+- **多于其他社区沟通,达到互帮互助引流的效果**
+
diff --git a/document/source/wiki/dinky/index.rst b/document/source/wiki/dinky/index.rst
new file mode 100644
index 0000000..f02a3c7
--- /dev/null
+++ b/document/source/wiki/dinky/index.rst
@@ -0,0 +1,7 @@
+dinky
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ README
diff --git a/document/source/wiki/evolution/Business.png b/document/source/wiki/evolution/Business.png
new file mode 100644
index 0000000..4adca85
Binary files /dev/null and b/document/source/wiki/evolution/Business.png differ
diff --git a/document/source/wiki/evolution/BusinessProcess.png b/document/source/wiki/evolution/BusinessProcess.png
new file mode 100644
index 0000000..5c3bc27
Binary files /dev/null and b/document/source/wiki/evolution/BusinessProcess.png differ
diff --git a/document/source/wiki/evolution/CHANGELOG b/document/source/wiki/evolution/CHANGELOG
new file mode 100644
index 0000000..0a2f68a
--- /dev/null
+++ b/document/source/wiki/evolution/CHANGELOG
@@ -0,0 +1,48 @@
+# PLATFORM FOR BDP 科学护城河
+
+# Version1
+
+```markdown
+
+1、美团Logan埋点
+
+2、采集中心代码编写
+
+3、维护kafka集群
+
+4、日志消费系统
+
+5、爬虫采集系统
+
+6、ETL清洗入ES, HBase
+
+7、电商分析
+
+8、上线demo
+```
+
+# Version2
+
+```markdown
+
+1、BUSINESS FOR ALL DATA PLATFORM 商业项目
+
+2、BUSINESS FOR ALL DATA PLATFORM 计算引擎
+
+3、DEVOPS FOR ALL DATA PLATFORM 运维引擎
+
+4、DATA GOVERN FOR ALL DATA PLATFORM 数据治理引擎
+
+5、DATA Integrate FOR ALL DATA PLATFORM 数据集成引擎
+
+6、AI FOR ALL DATA PLATFORM 人工智能引擎
+
+7、DATA ODS FOR ALL DATA PLATFORM 数据采集引擎
+
+8、OLAP FOR ALL DATA PLATFORM OLAP查询引擎
+
+9、OPTIMIZE FOR ALL DATA PLATFORM 性能优化引擎
+
+10、DATABASES FOR ALL DATA PLATFORM 分布式存储引擎
+
+```
\ No newline at end of file
diff --git a/document/source/wiki/evolution/CommunityEdition.png b/document/source/wiki/evolution/CommunityEdition.png
new file mode 100644
index 0000000..1f630a7
Binary files /dev/null and b/document/source/wiki/evolution/CommunityEdition.png differ
diff --git a/document/source/wiki/evolution/CustomScene.png b/document/source/wiki/evolution/CustomScene.png
new file mode 100644
index 0000000..3955018
Binary files /dev/null and b/document/source/wiki/evolution/CustomScene.png differ
diff --git a/document/source/wiki/evolution/Doris.png b/document/source/wiki/evolution/Doris.png
new file mode 100644
index 0000000..cb10215
Binary files /dev/null and b/document/source/wiki/evolution/Doris.png differ
diff --git a/document/source/wiki/evolution/Overview.png b/document/source/wiki/evolution/Overview.png
new file mode 100644
index 0000000..4829026
Binary files /dev/null and b/document/source/wiki/evolution/Overview.png differ
diff --git a/document/source/wiki/evolution/README.md b/document/source/wiki/evolution/README.md
new file mode 100644
index 0000000..1a79554
--- /dev/null
+++ b/document/source/wiki/evolution/README.md
@@ -0,0 +1,59 @@
+# 未来规划
+
+## 1、 AllData总述
+>
+> 一个基础平台基于Docker拉起20+种开源组件的启停,第二个是数据平台的产品
+>
+## 2、组件维护与升级
+>
+> 由于Apache很多开源项目发展节奏较快,目前暂时没有很好的版本切换的能力,维护不同组件的版本比较困难
+>
+## 3、定期升级维护最新的组件版本
+>
+> 并对已进行二次开发的组件进行适配升级到组件新版本,保证平滑兼容升级
+>
+## 4、apache社区组件版本
+>
+> DataSphereStudio: 1.1.0
+>
+> docker: 3.0.0.0-SNAPSHOT
+>
+> Dolphinscheduler: 3.0.0
+>
+> Atlas: 3.0.0-SNAPSHOT
+>
+> Griffin: 0.7.0-SNAPSHOT
+>
+> Kong: 3.0.0
+>
+> Flinkcdc: 2.3 RELEASE
+>
+> Flinkx: 1.12-SNAPSHOT
+>
+> Kylin: 4.0.2
+>
+> Presto: 0.276.2
+>
+> Hudi: v0.12.0
+>
+> Iceberg: v0.13.1
+>
+> Dlink: 0.6.7
+>
+> Linkis: 1.2.0
+>
+> Streamis: 0.2.0
+>
+> Doris: 1.0-SNAPSHOT
+>
+> DataX: X-version
+>
+> Backend: v1.2-SNAPSHOT
+>
+> Rancher: 2.6.7
+>
+> SREWorks: X-version
+>
+> Cube-Studio: X-version
+>
+> GraphScope: 0.17.0
diff --git a/document/source/wiki/evolution/RecommendSystem.png b/document/source/wiki/evolution/RecommendSystem.png
new file mode 100644
index 0000000..d3a59ac
Binary files /dev/null and b/document/source/wiki/evolution/RecommendSystem.png differ
diff --git a/document/source/wiki/evolution/RoadMap.png b/document/source/wiki/evolution/RoadMap.png
new file mode 100644
index 0000000..b6dd48e
Binary files /dev/null and b/document/source/wiki/evolution/RoadMap.png differ
diff --git a/document/source/wiki/evolution/TimeTravel.png b/document/source/wiki/evolution/TimeTravel.png
new file mode 100644
index 0000000..3880a92
Binary files /dev/null and b/document/source/wiki/evolution/TimeTravel.png differ
diff --git a/document/source/wiki/evolution/TreeMap.png b/document/source/wiki/evolution/TreeMap.png
new file mode 100644
index 0000000..536797e
Binary files /dev/null and b/document/source/wiki/evolution/TreeMap.png differ
diff --git a/document/source/wiki/evolution/ai-studio.png b/document/source/wiki/evolution/ai-studio.png
new file mode 100644
index 0000000..3493ac1
Binary files /dev/null and b/document/source/wiki/evolution/ai-studio.png differ
diff --git a/document/source/wiki/evolution/index.rst b/document/source/wiki/evolution/index.rst
new file mode 100644
index 0000000..5702eba
--- /dev/null
+++ b/document/source/wiki/evolution/index.rst
@@ -0,0 +1,7 @@
+evolution
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ README
diff --git a/document/source/wiki/hudi/README.md b/document/source/wiki/hudi/README.md
new file mode 100644
index 0000000..5adebe9
--- /dev/null
+++ b/document/source/wiki/hudi/README.md
@@ -0,0 +1,3 @@
+# Hudi性能测试报告
+
+> https://docs.qq.com/doc/DVFlVUExWd3Jva2ZK
\ No newline at end of file
diff --git a/document/source/wiki/hudi/index.rst b/document/source/wiki/hudi/index.rst
new file mode 100644
index 0000000..7fa2789
--- /dev/null
+++ b/document/source/wiki/hudi/index.rst
@@ -0,0 +1,7 @@
+hudi
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ README
diff --git a/document/source/wiki/index.rst b/document/source/wiki/index.rst
new file mode 100644
index 0000000..8d52cf1
--- /dev/null
+++ b/document/source/wiki/index.rst
@@ -0,0 +1,10 @@
+社区分享
+=================================
+
+.. toctree::
+ :maxdepth: 2
+
+ alldata/index
+ dinky/index
+ hudi/index
+ evolution/index
diff --git a/dts_quickstart.md b/dts_quickstart.md
new file mode 100644
index 0000000..207fbe2
--- /dev/null
+++ b/dts_quickstart.md
@@ -0,0 +1,155 @@
+# 数据集成单任务配置(使用datax)
+
+**1、安装datax**
+
+ 安装参考[linux安装dataX-CSDN博客](https://blog.csdn.net/hzp666/article/details/127350768)
+
+**2、配置datax同步mysql**
+> 配置mysql隔离级别,进入mysql终端
+>
+> SET GLOBAL transaction_isolation='READ-COMMITTED';
+
+配置数据源
+
+jdbc:mysql://16gmaster:33060/studio?useUnicode=true&characterEncoding=utf8&useLocalSessionState=true
+
+
+
+
+
+
+
+
+
+配置执行器
+
+
+
+
+
+配置任务模版
+
+
+
+
+
+配置单任务
+
+按步骤选择数据抽取库,数据合并库,映射字段,构建datax的可执行json文件,选择任务模版,点击下一步任务就创建完成了,可以在任务详情里查看
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+最后可以运行的json文件
+
+```json
+{
+ "job": {
+ "setting": {
+ "speed": {
+ "channel": 1,
+ "bytes": 0
+ },
+ "errorLimit": {
+ "record": 100
+ },
+ "restore": {
+ "maxRowNumForCheckpoint": 0,
+ "isRestore": false,
+ "restoreColumnName": "",
+ "restoreColumnIndex": 0
+ },
+ "log": {
+ "isLogger": false,
+ "level": "debug",
+ "path": "",
+ "pattern": ""
+ }
+ },
+ "content": [{
+ "reader": {
+ "name": "mysqlreader",
+ "parameter": {
+ "username": "root",
+ "password": "123456",
+ "column": [
+ "menu_id",
+ "pid",
+ "sub_count",
+ "type",
+ "title",
+ "name",
+ "component",
+ "menu_sort",
+ "icon",
+ "path",
+ "i_frame",
+ "cache",
+ "hidden",
+ "permission",
+ "create_by",
+ "update_by",
+ "create_time",
+ "update_time"
+ ],
+ "splitPk": "",
+ "connection": [{
+ "table": [
+ "sys_menu_source"
+ ],
+ "jdbcUrl": [
+ "jdbc:mysql://16gmaster:33060/studio?useUnicode=true&characterEncoding=utf8&useLocalSessionState=true"
+ ]
+ }]
+ }
+ },
+ "writer": {
+ "name": "mysqlwriter",
+ "parameter": {
+ "username": "root",
+ "password": "123456",
+ "writeMode": "insert",
+ "column": [
+ "menu_id",
+ "pid",
+ "sub_count",
+ "type",
+ "title",
+ "name",
+ "component",
+ "menu_sort",
+ "icon",
+ "path",
+ "i_frame",
+ "cache",
+ "hidden",
+ "permission",
+ "create_by",
+ "update_by",
+ "create_time",
+ "update_time"
+ ],
+ "connection": [{
+ "table": [
+ "sys_menu_target"
+ ],
+ "jdbcUrl": "jdbc:mysql://16gmaster:33060/studio?useUnicode=true&characterEncoding=utf8&useLocalSessionState=true"
+ }]
+ }
+ }
+ }]
+ }
+}
+```
+
+
diff --git a/install.md b/install.md
new file mode 100644
index 0000000..07dc16e
--- /dev/null
+++ b/install.md
@@ -0,0 +1,436 @@
+## 部署指南
+
+### 1、后端结构
+
+```
+├── studio
+│ ├── config(配置中心,必须启动)
+│ ├── eureka(注册中心,必须启动)
+│ ├── gateway(网关,必须启动)
+│ ├── install(脚本目录,数据库脚本必须)
+│ │ ├── 16gdata
+│ │ ├── 16gmaster
+│ │ │ ├──studio
+│ │ │ │ ├──studio-0.x.x.sql
+│ ├── modules(各模块目录)
+│ │ ├── codegen-service-parent(代码生成模块,可选启动)
+│ │ ├── data-market-service-parent(数据集市模块,可选启动)
+│ │ ├── data-masterdata-service-parent(主数据服务模块,可选启动)
+│ │ ├── data-metadata-service-parent(元数据管理模块,可选启动)
+│ │ ├── data-quality-service-parent(数据质量模块,可选启动)
+│ │ ├── data-standard-service-parent(数据标准模块,可选启动)
+│ │ ├── data-system-service-parent
+│ │ ├── data-visual-service-parent(数据可视化模块,可选启动)
+│ │ ├── email-service-parent(邮件管理模块,可选启动)
+│ │ ├── file-service-parent(文件管理模块,可选启动)
+│ │ ├── quartz-service-parent(定时任务模块,可选启动)
+│ │ ├── service-data-dts-parent(数据集成模块,可选启动)
+│ │ ├── system-service-parent(系统管理模块,必须启动)
+│ │ └── workflow-service-parent(工作流模块,可选启动)
+│ ├── pom.xml
+
+```
+
+### 2、前端结构
+
+
+```
+│ ── micro-ui
+ ├── public // 公共文件
+ │ ├── favicon.ico // favicon图标
+ │ ├── index.html // html模板
+ │ └── robots.txt //爬虫协议
+ ├── src // 源代码
+ │ ├── App.vue
+ │ ├── api // 所有请求
+ │ ├── assets// 主题 字体等静态资源
+ │ ├── components // 全局公用组件
+ │ ├── directive // 全局指令
+ │ ├── filters
+ │ ├── icons
+ │ ├── layout // 布局
+ │ ├── main.js // 入口 加载组件 初始化等
+ │ ├── mixins
+ │ ├── router // 路由
+ │ ├── settings.js // 系统配置
+ │ ├── store // 全局 store管理
+ │ ├── styles // 样式
+ │ ├── utils // 全局公用方法
+ │ ├── vendor
+ │ └── views // view页面
+ │ ├── components
+ │ ├── dashboard
+ │ ├── dts
+ │ ├── features
+ │ ├── generator
+ │ ├── govern
+ │ ├── home.vue
+ │ ├── lakehouse
+ │ ├── login.vue
+ │ ├── market
+ │ ├── masterdata
+ │ ├── metadata
+ │ ├── mnt
+ │ ├── monitor
+ │ ├── nested
+ │ ├── quality
+ │ ├── standard
+ │ ├── system
+ │ ├── tools
+ │ ├── visual
+ │ └── workflow
+ └── vue.config.js
+ ├── package.json
+ ├── plopfile.js
+ ├── postcss.config.js
+
+```
+
+
+### 3、准备工作
+
+
+```
+JDK >= 1.8
+Mysql >= 5.7.0 (推荐5.7及以上版本)
+Redis >= 3.0
+Maven >= 3.0
+Node >= 10.15.3
+RabbitMQ >= 3.0.x
+```
+> 使用Mysql 8的用户注意导入数据时的编码格式问题
+
+### 4、本地启动/运行系统
+
+> 首先确保启动rabbitmq,mysql,redis已经启动
+
+#### 4.1 后端运行
+
+1、前往GitHub项目页面(https://github.com/alldatacenter/alldata)
+推荐使用版本控制方式下载解压到工作目录或IDEA直接从VCS获取项目代码,便于同步最新社区版改动, alldata/studio/为项目前后端存放路径。
+
+2、项目导入到IDEA后,会自动加载Maven依赖包,初次加载会比较慢(根据自身网络情况而定)
+
+3、创建数据库studio:到 `factory/studio/install/sql`目录下sql数据脚本,把` studio.sql`和`studio-v0.x.x.sql`导入本地或线上Mysql数据库
+
+4、导入BI sql, 参考alldata/bi_quickstart.md
+
+5、修改该文件 `alldata/studio/config/src/main/resources/config/application-common-dev.yml`的rabbitmq,mysql,redis为自己的服务
+
+6、打开运行基础模块(启动没有先后顺序)
+
+```
+DataxEurekaApplication.java(注册中心模块 必须)
+DataxConfigApplication.java(配置中心模块 必须)
+DataxGatewayApplication.java(网关模块 必须)
+SystemServiceApplication.java(系统模块 必须,不启动无法登录)
+```
+其他模块可根据需求,自行决定启动
+
+5.1 启动Eurake项目
+
+1. 找到factory/studio/eureka/src/main/java/cn/datax/eureka/DataxEurekaApplication.java 运行启动
+
+2. 浏览器访问 http://localhost:8610/,看到以下页面表示启动成功
+
+5.2 启动Config项目
+1. 修改bootstrap.yml文件,本地运行时eureka配置处,改成localhost
+2. 找到factory/studio/config/src/main/java/cn/datax/config/DataxConfigApplication.java,运行启动
+
+5.3 启动Gateway项目
+1. 修改bootstrap.yml文件,本地运行时eureka配置处,改成localhost
+
+2. 找到factory/studio/gateway/src/main/java/cn/datax/gateway/DataxGatewayApplication.java,启动项目
+
+3. 启动完后,可以在Eureka中发现刚才启动的服务
+
+5.4 启动SystemService项目,本地运行时eureka配置处,改成localhost。及其他项目同理。
+
+
+#### 4.2 前端运行
+```
+cd alldata/studio/micro-ui
+npm run dev
+```
+启动成功,会自动弹出浏览器登录页
+
+
+#### 注意目前视频能看到的功能都已开源,若发现“数据集成”菜单没有.
+#### 可只导入factory/studio/install/sql下的studio.sql + studio-v0.x.x + 数据集成。
+#### 其他菜单若发现没有的话,也可自行配置,具体参考 https://github.com/alldatacenter/alldata/issues/489
+
+
+#### 4.3 启动SystemService项目,本地运行时eureka配置处,改成localhost。及其他项目同理。
+```
+系统管理 - system-service-parent ~ system-service ~ SystemServiceApplication
+数据集成 - service-data-dts-parent ~ service-data-dts ~ DataDtsServiceApplication
+元数据管理 - data-metadata-service-parent ~ data-metadata-service ~ DataxMetadataApplication
+元数据管理 - data-metadata-service-parent ~ data-metadata-service-console ~ DataxConsoleApplication
+数据标准 - data-standard-service-parent ~ data-standard-service ~ DataxStandardApplication
+数据质量 - data-quality-service-parent ~ data-quality-service ~ DataxQualityApplication
+数据资产 - data-masterdata-service-parent ~ data-masterdata-service ~ DataxMasterdataApplication
+数据市场 - data-market-service-parent ~ data-market-service ~ DataxMarketApplication
+数据市场 - data-market-service-parent ~ data-market-service-integration ~ DataxIntegrationApplication
+数据市场 - data-market-service-parent ~ data-market-service-mapping ~ DataxMappingApplication
+数据对比 - data-compare-service-parent ~ data-compare-service ~ DataCompareApplication
+BI报表 - data-visual-service-parent ~ data-visual-service ~ DataxVisualApplication
+流程编排 - workflow-service-parent ~ workflow-service ~ DataxWorkflowApplication
+系统监控 - system-service-parent ~ system-service ~ SystemServiceApplication
+批量/定时任务 - quartz-service-parent ~ quartz-service ~ DataxQuartzApplication
+代码生成 - codegen-service-parent ~ codegen-service ~ DataxCodeGenApplication
+邮件服务 - email-service-parent ~ email-service ~ DataxMailApplication
+文件服务 - file-service-parent ~ file-service ~ DataxFileApplication
+```
+
+### 5、服务器集群部署
+| 16gmaster | port | ip |
+|--------------------------|------| -------------- |
+| system-service | 8000 | 16gmaster |
+| data-market-service | 8822 | 16gmaster |
+| service-data-integration | 8824 | 16gmaster |
+| data-metadata-service | 8820 | 16gmaster |
+| data-system-service | 8810 | 16gmaster |
+| service-data-dts | 9536 | 16gmaster |
+| config | 8611 | 16gmaster |
+
+| 16gslave | port | ip |
+|-------------------------------| ---- | -------------- |
+| eureka | 8610 | 16gslave |
+| service-workflow | 8814 | 16gslave |
+| data-metadata-service-console | 8821 | 16gslave |
+| service-data-mapping | 8823 | 16gslave |
+| data-masterdata-service | 8828 | 16gslave |
+| data-quality-service | 8826 | 16gslave |
+
+| 16gdata | port | ip |
+|-----------------------| ---- | -------------- |
+| data-standard-service | 8825 | 16gdata |
+| data-visual-service | 8827 | 16gdata |
+| email-service | 8812 | 16gdata |
+| file-service | 8811 | 16gdata |
+| quartz-service | 8813 | 16gdata |
+| gateway | 9538 | 16gslave |
+
+
+### 6、部署方式
+
+> 数据库版本为 **mysql5.7** 及以上版本
+#### 1、`studio`数据库初始化
+>
+> 1.1 source install/sql/studio.sql
+> 1.2 source install/sql/studio-v0.x.x.sql
+> 1.3 导入BI sql, 参考alldata/bi_quickstart.md
+
+#### 2、修改 **config** 配置中心
+
+> **config** 文件夹下的配置文件, 修改 **redis**, **mysql** 和 **rabbitmq** 的配置信息
+>
+#### 3、项目根目录下执行
+```
+1、缺失aspose-words,要手动安装到本地仓库
+2、cd alldata/studio/common
+3、安装命令:windows使用git bash执行, mac直接执行以下命令
+4、mvn install:install-file -Dfile=aspose-words-20.3.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=20.3 -Dpackaging=jar
+5、安装成功重新刷新依赖,重新打包
+```
+> cd alldata/studio/common
+> mvn install:install-file -Dfile=/alldata/studio/common/aspose-words-20.3.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=20.3 -Dpackaging=jar
+> mvn clean install -DskipTests && mvn clean package -DskipTests
+> 获取安装包build/studio-release-0.4.x.tar.gz
+>
+> 上传服务器解压
+>
+#### 4、部署`stuido`[后端]
+#### 单节点启动[All In One]
+
+> 1、启动eureka on `16gslave`
+>
+> 2、启动config on `16gmaster`
+>
+> 3、启动gateway on `16gdata`
+>
+> 4、启动其他Jar
+
+#### 5、三节点启动[16gmaster, 16gslave, 16gdata]
+> 1. 单独启动 eureka on `16gslave`
+>
+> 2. 单独启动config on `16gmaster`
+>
+> 3. 单独启动gateway on `16gdata`
+>
+> 4. 启动`16gslave`, sh start16gslave.sh
+>
+> 5. 启动`16gdata`, sh start16gdata.sh
+>
+> 6. 启动`16gmaster`, sh start16gmaster.sh
+
+#### 6、部署`studio`[前端]:
+#### 前端部署
+
+#### 安装依赖
+
+> 依次安装:
+> nvm install v10.15.3 && nvm use v10.15.3
+
+> npm install -g @vue/cli
+
+> npm install script-loader
+
+> npm install jsonlint
+
+> npm install vue2-jsoneditor
+
+> npm install
+
+> npm run build:prod [生产]
+>
+> 生产环境启动前端micro-ui项目, 需要[配置nginx]
+```markdown
+# For more information on configuration, see:
+# * Official English Documentation: http://nginx.org/en/docs/
+# * Official Russian Documentation: http://nginx.org/ru/docs/
+
+user nginx;
+worker_processes auto;
+error_log /var/log/nginx/error.log;
+pid /run/nginx.pid;
+
+# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
+include /usr/share/nginx/modules/*.conf;
+
+events {
+worker_connections 1024;
+}
+
+http {
+log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+'$status $body_bytes_sent "$http_referer" '
+'"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 4096;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ # Load modular configuration files from the /etc/nginx/conf.d directory.
+ # See http://nginx.org/en/docs/ngx_core_module.html#include
+ # for more information.
+ include /etc/nginx/conf.d/*.conf;
+ server {
+ listen 80;
+ server_name 16gmaster;
+ add_header Access-Control-Allow-Origin *;
+ add_header Access-Control-Allow-Headers X-Requested-With;
+ add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
+ location / {
+ root /studio/micro-ui/dist;
+ index index.html;
+ try_files $uri $uri/ /index.html;
+ }
+ location /api/ {
+ proxy_pass http://16gdata:9538/;
+ proxy_set_header Host $proxy_host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ }
+ }
+}
+```
+> 测试环境启动前端micro-ui项目
+>
+> npm run dev [测试]
+>
+> 访问`studio`页面
+>
+> curl http://localhost:8013
+>
+> 用户名:admin 密码:123456
+
+### 7、数据集成配置教程
+
+> 先找到用户管理-菜单管理, 新增【数据集成】目录
+>
+> 新增【数据集成】下面的菜单, 菜单各项按如下配置输入, 之后进入角色管理
+>
+> 配置admin账号的目录数据权限, 选中刚才新增的数据集成目录及里面的菜单, 刷新或重新登录即可访问【数据集成】
+
+
+
+
+
+
+
+
+
+
+
+
+### 8、常见问题
+```
+前置 -
+1、启动前是删除了pom.xml;
+2、本地是V16版本的nodejs;
+
+运行 -
+1、启动后端相关服务;
+2、启动前端npm run dev,报错:
+multi ./node modules/.pnpm/webpack-dev-server3.1.3 webpack04.28.4/node modules/webpack-dev-server/clienthtp://192.168.0.118:8013/sockjs-node(webpack)/hot/dev-server.js ./src/main.js
+Module not found: Error: Can't resolvebabel-loader'in D: workspaceldatacenter workspacelscit-datacenter-ui
+
+原因 -
+前端UI对应nodejs版本是v10.15.3 , 需要切换版本,为开发方便,一遍采用nvm进行管理
+1、卸载nodejs;
+2、安装nvm - https://www.jianshu.com/p/13c0b3ca7c71
+3、安装v10.15.3版本: nvm install v10.15.3
+4、根据实际切换版本:nvm use v10.15.3
+5、安装依赖:npm install
+6、启动前端:npm run dev
+```
+
+```
+前置 -
+1、数据集成教程
+
+运行 -
+1、数据集成教程
+
+原因 -
+1、教程
+https://github.com/alldatacenter/alldata/blob/master/studio/modules/service-data-dts-parent/DTS_QuickStart.md
+
+```
+
+```
+前置 -
+1、数据集成教程
+
+运行 -
+1、数据集成教程
+
+原因 -
+1、教程
+2、依赖datax,安装datax: https://blog.csdn.net/qq_18896247/article/details/123127487
+3、https://github.com/alldatacenter/alldata/blob/master/studio/modules/service-data-dts-parent/DTS_QuickStart.md
+
+```
+
+```
+前置 -
+1、元数据数据库文档下载
+
+运行 -
+1、元数据数据库文档下载,依赖报错
+
+原因 -
+1、缺失aspose-words,要手动安装到本地仓库
+2、cd alldata/studio/common
+3、安装命令:windows使用git bash执行, mac直接执行以下命令
+4、mvn install:install-file -Dfile=aspose-words-20.3.jar -DgroupId=com.aspose -DartifactId=aspose-words -Dversion=20.3 -Dpackaging=jar
+5、安装成功重新刷新依赖,重新打包
+```
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cdeee2c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,77 @@
+
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.2.6.RELEASE
+
+
+ com.platform
+ alldata
+ 0.4.x
+ pom
+
+ AllData
+ https://github.com/alldatacenter/alldata
+
+
+ UTF-8
+ 1.8
+ 1.8
+
+
+
+ studio
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+ alldata-release-${project.version}
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-war-plugin
+ 3.2.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+
+
+
+
diff --git a/studio/.gitignore b/studio/.gitignore
new file mode 100644
index 0000000..28fa38a
--- /dev/null
+++ b/studio/.gitignore
@@ -0,0 +1,8 @@
+### IDEA ###
+.idea/*
+*.iml
+*/target/*
+*/*.iml
+/.gradle/
+application.pid
+spy.log
\ No newline at end of file
diff --git a/studio/.gitkeep b/studio/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/studio/assembly/pom.xml b/studio/assembly/pom.xml
new file mode 100644
index 0000000..142b057
--- /dev/null
+++ b/studio/assembly/pom.xml
@@ -0,0 +1,40 @@
+
+
+
+ studio
+ com.platform
+ 0.4.x
+
+ 4.0.0
+
+ assembly
+ pom
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.6
+
+
+ package
+
+ single
+
+
+ ${project.parent.artifactId}-release-${project.version}
+ false
+
+ src/main/assembly/package.xml
+
+ ${project.parent.basedir}/build/
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/studio/assembly/src/main/assembly/package.xml b/studio/assembly/src/main/assembly/package.xml
new file mode 100644
index 0000000..f70fe54
--- /dev/null
+++ b/studio/assembly/src/main/assembly/package.xml
@@ -0,0 +1,424 @@
+
+ ${project.version}
+
+
+ tar.gz
+
+
+ false
+
+
+ ${project.parent.basedir}/install/16gmaster
+ ./16gmaster
+
+
+
+ ${project.parent.basedir}/install/sql
+ ./sql
+
+
+
+ ${project.parent.basedir}/install/16gslave
+ ./16gslave
+
+
+ ${project.parent.basedir}/install/16gdata
+ ./16gdata
+
+
+
+
+ ${project.parent.basedir}/install
+ ./
+
+ README.md
+
+
+
+
+
+ ${project.parent.basedir}/config/target
+
+ ./16gmaster/config
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/config/target/classes
+
+ ./16gmaster/config
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/eureka/target
+
+ ./16gslave/eureka
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/eureka/target/classes
+
+ ./16gslave/eureka
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/gateway/target
+ ./16gdata/gateway
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/gateway/target/classes
+ ./16gdata/gateway
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service-integration/target
+
+ ./16gmaster/data-market-service-integration
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service-integration/target/classes
+
+ ./16gmaster/data-market-service-integration
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-compare-service-parent/data-compare-service/target
+
+ ./16gmaster/data-compare-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-compare-service-parent/data-compare-service/target/classes
+
+ ./16gmaster/data-compare-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-cluster-service-parent/data-cluster-service/target
+
+ ./16gslave/data-cluster-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-cluster-service-parent/data-cluster-service/target/classes
+
+ ./16gslave/data-cluster-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-system-service-parent/data-system-service/target
+
+ ./16gmaster/data-system-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-system-service-parent/data-system-service/target/classes
+
+ ./16gmaster/data-system-service
+
+ bootstrap.yml
+
+
+
+
+
+ ${project.parent.basedir}/modules/service-data-dts-parent/service-data-dts/target
+
+ ./16gmaster/service-data-dts
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/service-data-dts-parent/service-data-dts/target/classes
+
+ ./16gmaster/service-data-dts
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/system-service-parent/system-service/target
+
+ ./16gmaster/system-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/system-service-parent/system-service/target/classes
+
+ ./16gmaster/system-service
+
+ bootstrap.yml
+
+
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service/target
+
+ ./16gmaster/data-market-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service/target/classes
+
+ ./16gmaster/data-market-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service-mapping/target
+
+ ./16gslave/data-market-service-mapping
+
+ *.jar
+
+
+
+ ${project.parent.basedir}/modules/data-market-service-parent/data-market-service-mapping/target/classes
+
+ ./16gslave/data-market-service-mapping
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-masterdata-service-parent/data-masterdata-service/target
+
+ ./16gslave/data-masterdata-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-masterdata-service-parent/data-masterdata-service/target/classes
+
+ ./16gslave/data-masterdata-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-metadata-service-parent/data-metadata-service-console/target
+
+ ./16gslave/data-metadata-service-console
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-metadata-service-parent/data-metadata-service-console/target/classes
+
+ ./16gslave/data-metadata-service-console
+
+ bootstrap.yml
+
+
+
+
+
+ ${project.parent.basedir}/modules/data-metadata-service-parent/data-metadata-service/target
+
+ ./16gmaster/data-metadata-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-metadata-service-parent/data-metadata-service/target/classes
+
+ ./16gmaster/data-metadata-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-quality-service-parent/data-quality-service/target
+
+ ./16gslave/data-quality-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-quality-service-parent/data-quality-service/target/classes
+
+ ./16gslave/data-quality-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/data-standard-service-parent/data-standard-service/target
+
+ ./16gdata/data-standard-service
+
+ *.jar
+
+
+
+ ${project.parent.basedir}/modules/data-standard-service-parent/data-standard-service/target/classes
+
+ ./16gdata/data-standard-service
+
+ bootstrap.yml
+
+
+
+
+
+ ${project.parent.basedir}/modules/data-visual-service-parent/data-visual-service/target
+
+ ./16gdata/data-visual-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/data-visual-service-parent/data-visual-service/target/classes
+
+ ./16gdata/data-visual-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/quartz-service-parent/quartz-service/target
+
+ ./16gdata/quartz-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/quartz-service-parent/quartz-service/target/classes
+
+ ./16gdata/quartz-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/workflow-service-parent/workflow-service/target
+
+ ./16gslave/workflow-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/workflow-service-parent/workflow-service/target/classes
+
+ ./16gslave/workflow-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/modules/email-service-parent/email-service/target
+
+ ./16gdata/email-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/email-service-parent/email-service/target/classes
+
+ ./16gdata/email-service
+
+ bootstrap.yml
+
+
+
+
+
+ ${project.parent.basedir}/modules/file-service-parent/file-service/target
+
+ ./16gdata/file-service
+
+ *.jar
+
+
+
+
+ ${project.parent.basedir}/modules/file-service-parent/file-service/target/classes
+
+ ./16gdata/file-service
+
+ bootstrap.yml
+
+
+
+
+ ${project.parent.basedir}/ui
+ ./16gmaster/ui
+
+
+
+
\ No newline at end of file
diff --git a/studio/box/pom.xml b/studio/box/pom.xml
new file mode 100644
index 0000000..2a7d31e
--- /dev/null
+++ b/studio/box/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+ studio
+ com.platform
+ 0.4.x
+
+ 4.0.0
+
+ box
+ box
+
+
+ 1.4.7
+ 7.9.3
+ 4.22.57.ALL
+
+
+
+
+
+ com.platform
+ logging
+ 0.4.x
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ javax.mail
+ mail
+ ${mail.version}
+
+
+
+
+ com.qiniu
+ qiniu-java-sdk
+ ${qiniu.version}
+
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+
+
+ com.alipay.sdk
+ alipay-sdk-java
+ ${alipay.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ com.platform
+ common-mybatis
+ 0.4.x
+
+
+
+
diff --git a/studio/box/src/main/java/com/platform/config/MultipartConfig.java b/studio/box/src/main/java/com/platform/config/MultipartConfig.java
new file mode 100644
index 0000000..39a14cf
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/config/MultipartConfig.java
@@ -0,0 +1,34 @@
+
+package com.platform.config;
+
+import org.springframework.boot.web.servlet.MultipartConfigFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import javax.servlet.MultipartConfigElement;
+import java.io.File;
+
+/**
+ * @date 2023-01-27
+ * @author AllDataDC
+ * https://blog.csdn.net/llibin1024530411/article/details/79474953
+ */
+@Configuration
+public class MultipartConfig {
+
+ /**
+ * 文件上传临时路径
+ */
+ @Bean
+ MultipartConfigElement multipartConfigElement() {
+ MultipartConfigFactory factory = new MultipartConfigFactory();
+ String location = System.getProperty("user.home") + "/.studio/file/tmp";
+ File tmpFile = new File(location);
+ if (!tmpFile.exists()) {
+ if (!tmpFile.mkdirs()) {
+ System.out.println("create was not successful.");
+ }
+ }
+ factory.setLocation(location);
+ return factory.createMultipartConfig();
+ }
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/domain/AlipayConfig.java b/studio/box/src/main/java/com/platform/domain/AlipayConfig.java
new file mode 100644
index 0000000..3d31eae
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/AlipayConfig.java
@@ -0,0 +1,62 @@
+
+package com.platform.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 支付宝配置类
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+@Entity
+@Table(name = "tool_alipay_config")
+public class AlipayConfig implements Serializable {
+
+ @Id
+ @Column(name = "config_id")
+ @ApiModelProperty(value = "ID", hidden = true)
+ private Long id;
+
+ @NotBlank
+ @ApiModelProperty(value = "应用ID")
+ private String appId;
+
+ @NotBlank
+ @ApiModelProperty(value = "商户私钥")
+ private String privateKey;
+
+ @NotBlank
+ @ApiModelProperty(value = "支付宝公钥")
+ private String publicKey;
+
+ @ApiModelProperty(value = "签名方式")
+ private String signType="RSA2";
+
+ @Column(name = "gateway_url")
+ @ApiModelProperty(value = "支付宝开放安全地址", hidden = true)
+ private String gatewayUrl = "https://openapi.alipaydev.com/gateway.do";
+
+ @ApiModelProperty(value = "编码", hidden = true)
+ private String charset= "utf-8";
+
+ @NotBlank
+ @ApiModelProperty(value = "异步通知地址")
+ private String notifyUrl;
+
+ @NotBlank
+ @ApiModelProperty(value = "订单完成后返回的页面")
+ private String returnUrl;
+
+ @ApiModelProperty(value = "类型")
+ private String format="JSON";
+
+ @NotBlank
+ @ApiModelProperty(value = "商户号")
+ private String sysServiceProviderId;
+
+}
diff --git a/studio/box/src/main/java/com/platform/domain/EmailConfig.java b/studio/box/src/main/java/com/platform/domain/EmailConfig.java
new file mode 100644
index 0000000..f308fac
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/EmailConfig.java
@@ -0,0 +1,44 @@
+
+package com.platform.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 邮件配置类,数据存覆盖式存入数据存
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Entity
+@Data
+@Table(name = "tool_email_config")
+public class EmailConfig implements Serializable {
+
+ @Id
+ @Column(name = "config_id")
+ @ApiModelProperty(value = "ID", hidden = true)
+ private Long id;
+
+ @NotBlank
+ @ApiModelProperty(value = "邮件服务器SMTP地址")
+ private String host;
+
+ @NotBlank
+ @ApiModelProperty(value = "邮件服务器 SMTP 端口")
+ private String port;
+
+ @NotBlank
+ @ApiModelProperty(value = "发件者用户名")
+ private String user;
+
+ @NotBlank
+ @ApiModelProperty(value = "密码")
+ private String pass;
+
+ @NotBlank
+ @ApiModelProperty(value = "收件人")
+ private String fromUser;
+}
diff --git a/studio/box/src/main/java/com/platform/domain/LocalStorage.java b/studio/box/src/main/java/com/platform/domain/LocalStorage.java
new file mode 100644
index 0000000..6be69c3
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/LocalStorage.java
@@ -0,0 +1,59 @@
+
+package com.platform.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.*;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.bean.copier.CopyOptions;
+import com.platform.base.BaseEntity;
+import javax.persistence.*;
+import java.io.Serializable;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Getter
+@Setter
+@Entity
+@Table(name="tool_local_storage")
+@NoArgsConstructor
+public class LocalStorage extends BaseEntity implements Serializable {
+
+ @Id
+ @Column(name = "storage_id")
+ @ApiModelProperty(value = "ID", hidden = true)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @ApiModelProperty(value = "真实文件名")
+ private String realName;
+
+ @ApiModelProperty(value = "文件名")
+ private String name;
+
+ @ApiModelProperty(value = "后缀")
+ private String suffix;
+
+ @ApiModelProperty(value = "路径")
+ private String path;
+
+ @ApiModelProperty(value = "类型")
+ private String type;
+
+ @ApiModelProperty(value = "大小")
+ private String size;
+
+ public LocalStorage(String realName,String name, String suffix, String path, String type, String size) {
+ this.realName = realName;
+ this.name = name;
+ this.suffix = suffix;
+ this.path = path;
+ this.type = type;
+ this.size = size;
+ }
+
+ public void copy(LocalStorage source){
+ BeanUtil.copyProperties(source,this, CopyOptions.create().setIgnoreNullValue(true));
+ }
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/domain/QiniuConfig.java b/studio/box/src/main/java/com/platform/domain/QiniuConfig.java
new file mode 100644
index 0000000..f981b18
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/QiniuConfig.java
@@ -0,0 +1,55 @@
+
+package com.platform.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import javax.persistence.*;
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+/**
+ * 七牛云对象存储配置类
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+@Entity
+@Table(name = "tool_qiniu_config")
+public class QiniuConfig implements Serializable {
+
+ @Id
+ @Column(name = "config_id")
+ @ApiModelProperty(value = "ID")
+ private Long id;
+
+ @NotBlank
+ @ApiModelProperty(value = "accessKey")
+ private String accessKey;
+
+ @NotBlank
+ @ApiModelProperty(value = "secretKey")
+ private String secretKey;
+
+ @NotBlank
+ @ApiModelProperty(value = "存储空间名称作为唯一的 Bucket 识别符")
+ private String bucket;
+
+ /**
+ * Zone表示与机房的对应关系
+ * 华东 Zone.zone0()
+ * 华北 Zone.zone1()
+ * 华南 Zone.zone2()
+ * 北美 Zone.zoneNa0()
+ * 东南亚 Zone.zoneAs0()
+ */
+ @NotBlank
+ @ApiModelProperty(value = "Zone表示与机房的对应关系")
+ private String zone;
+
+ @NotBlank
+ @ApiModelProperty(value = "外链域名,可自定义,需在七牛云绑定")
+ private String host;
+
+ @ApiModelProperty(value = "空间类型:公开/私有")
+ private String type = "公开";
+}
diff --git a/studio/box/src/main/java/com/platform/domain/QiniuContent.java b/studio/box/src/main/java/com/platform/domain/QiniuContent.java
new file mode 100644
index 0000000..4b8c99e
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/QiniuContent.java
@@ -0,0 +1,50 @@
+
+package com.platform.domain;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.annotations.UpdateTimestamp;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.sql.Timestamp;
+
+/**
+ * 上传成功后,存储结果
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+@Entity
+@Table(name = "tool_qiniu_content")
+public class QiniuContent implements Serializable {
+
+ @Id
+ @Column(name = "content_id")
+ @ApiModelProperty(value = "ID", hidden = true)
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ @Column(name = "name")
+ @ApiModelProperty(value = "文件名")
+ private String key;
+
+ @ApiModelProperty(value = "空间名")
+ private String bucket;
+
+ @ApiModelProperty(value = "大小")
+ private String size;
+
+ @ApiModelProperty(value = "文件地址")
+ private String url;
+
+ @ApiModelProperty(value = "文件类型")
+ private String suffix;
+
+ @ApiModelProperty(value = "空间类型:公开/私有")
+ private String type = "公开";
+
+ @UpdateTimestamp
+ @ApiModelProperty(value = "创建或更新时间")
+ @Column(name = "update_time")
+ private Timestamp updateTime;
+}
diff --git a/studio/box/src/main/java/com/platform/domain/vo/EmailVo.java b/studio/box/src/main/java/com/platform/domain/vo/EmailVo.java
new file mode 100644
index 0000000..2777c50
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/vo/EmailVo.java
@@ -0,0 +1,30 @@
+
+package com.platform.domain.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+/**
+ * 发送邮件时,接收参数的类
+ * @author AllDataDC
+ * @date 2023-01-27 12:02:14
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EmailVo {
+
+ /** 收件人,支持多个收件人 */
+ @NotEmpty
+ private List tos;
+
+ @NotBlank
+ private String subject;
+
+ @NotBlank
+ private String content;
+}
diff --git a/studio/box/src/main/java/com/platform/domain/vo/TradeVo.java b/studio/box/src/main/java/com/platform/domain/vo/TradeVo.java
new file mode 100644
index 0000000..8e27643
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/domain/vo/TradeVo.java
@@ -0,0 +1,49 @@
+
+package com.platform.domain.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import javax.validation.constraints.NotBlank;
+import java.sql.Date;
+import java.sql.Timestamp;
+
+/**
+ * 交易详情,按需应该存入数据库,这里存入数据库,仅供临时测试
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+public class TradeVo {
+
+ /** (必填)商品描述 */
+ @NotBlank
+ private String body;
+
+ /** (必填)商品名称 */
+ @NotBlank
+ private String subject;
+
+ /** (必填)商户订单号,应该由后台生成 */
+ @ApiModelProperty(hidden = true)
+ private String outTradeNo;
+
+ /** (必填)第三方订单号 */
+ @ApiModelProperty(hidden = true)
+ private String tradeNo;
+
+ /** (必填)价格 */
+ @NotBlank
+ private String totalAmount;
+
+ /** 订单状态,已支付,未支付,作废 */
+ @ApiModelProperty(hidden = true)
+ private String state;
+
+ /** 创建时间,存入数据库时需要 */
+ @ApiModelProperty(hidden = true)
+ private Timestamp createTime;
+
+ /** 作废时间,存入数据库时需要 */
+ @ApiModelProperty(hidden = true)
+ private Date cancelTime;
+}
diff --git a/studio/box/src/main/java/com/platform/repository/AliPayRepository.java b/studio/box/src/main/java/com/platform/repository/AliPayRepository.java
new file mode 100644
index 0000000..cb29dcd
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/repository/AliPayRepository.java
@@ -0,0 +1,12 @@
+
+package com.platform.repository;
+
+import com.platform.domain.AlipayConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface AliPayRepository extends JpaRepository {
+}
diff --git a/studio/box/src/main/java/com/platform/repository/EmailRepository.java b/studio/box/src/main/java/com/platform/repository/EmailRepository.java
new file mode 100644
index 0000000..2a276d6
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/repository/EmailRepository.java
@@ -0,0 +1,12 @@
+
+package com.platform.repository;
+
+import com.platform.domain.EmailConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface EmailRepository extends JpaRepository {
+}
diff --git a/studio/box/src/main/java/com/platform/repository/LocalStorageRepository.java b/studio/box/src/main/java/com/platform/repository/LocalStorageRepository.java
new file mode 100644
index 0000000..80c8824
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/repository/LocalStorageRepository.java
@@ -0,0 +1,13 @@
+
+package com.platform.repository;
+
+import com.platform.domain.LocalStorage;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+public interface LocalStorageRepository extends JpaRepository, JpaSpecificationExecutor {
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/repository/QiNiuConfigRepository.java b/studio/box/src/main/java/com/platform/repository/QiNiuConfigRepository.java
new file mode 100644
index 0000000..6fce0b9
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/repository/QiNiuConfigRepository.java
@@ -0,0 +1,22 @@
+
+package com.platform.repository;
+
+import com.platform.domain.QiniuConfig;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface QiNiuConfigRepository extends JpaRepository {
+
+ /**
+ * 编辑类型
+ * @param type
+ */
+ @Modifying
+ @Query(value = "update QiniuConfig set type = ?1")
+ void update(String type);
+}
diff --git a/studio/box/src/main/java/com/platform/repository/QiniuContentRepository.java b/studio/box/src/main/java/com/platform/repository/QiniuContentRepository.java
new file mode 100644
index 0000000..3f2363e
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/repository/QiniuContentRepository.java
@@ -0,0 +1,20 @@
+
+package com.platform.repository;
+
+import com.platform.domain.QiniuContent;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface QiniuContentRepository extends JpaRepository, JpaSpecificationExecutor {
+
+ /**
+ * 根据key查询
+ * @param key 文件名
+ * @return QiniuContent
+ */
+ QiniuContent findByKey(String key);
+}
diff --git a/studio/box/src/main/java/com/platform/rest/AliPayController.java b/studio/box/src/main/java/com/platform/rest/AliPayController.java
new file mode 100644
index 0000000..cef8e50
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/rest/AliPayController.java
@@ -0,0 +1,121 @@
+
+package com.platform.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import com.platform.annotation.rest.AnonymousAccess;
+import com.platform.annotation.Log;
+import com.platform.annotation.rest.AnonymousGetMapping;
+import com.platform.domain.vo.TradeVo;
+import com.platform.domain.AlipayConfig;
+import com.platform.utils.AliPayStatusEnum;
+import com.platform.utils.AlipayUtils;
+import com.platform.service.AliPayService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import springfox.documentation.annotations.ApiIgnore;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
+import java.util.Map;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/aliPay")
+@Api(tags = "工具:支付宝管理")
+public class AliPayController {
+
+ private final AlipayUtils alipayUtils;
+ private final AliPayService alipayService;
+
+ @GetMapping
+ public ResponseEntity queryAliConfig() {
+ return new ResponseEntity<>(alipayService.find(), HttpStatus.OK);
+ }
+
+ @Log("配置支付宝")
+ @ApiOperation("配置支付宝")
+ @PutMapping
+ public ResponseEntity updateAliPayConfig(@Validated @RequestBody AlipayConfig alipayConfig) {
+ alipayService.config(alipayConfig);
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @Log("支付宝PC网页支付")
+ @ApiOperation("PC网页支付")
+ @PostMapping(value = "/toPayAsPC")
+ public ResponseEntity toPayAsPc(@Validated @RequestBody TradeVo trade) throws Exception {
+ AlipayConfig aliPay = alipayService.find();
+ trade.setOutTradeNo(alipayUtils.getOrderCode());
+ String payUrl = alipayService.toPayAsPc(aliPay, trade);
+ return ResponseEntity.ok(payUrl);
+ }
+
+ @Log("支付宝手机网页支付")
+ @ApiOperation("手机网页支付")
+ @PostMapping(value = "/toPayAsWeb")
+ public ResponseEntity toPayAsWeb(@Validated @RequestBody TradeVo trade) throws Exception {
+ AlipayConfig alipay = alipayService.find();
+ trade.setOutTradeNo(alipayUtils.getOrderCode());
+ String payUrl = alipayService.toPayAsWeb(alipay, trade);
+ return ResponseEntity.ok(payUrl);
+ }
+
+ @ApiIgnore
+ @AnonymousGetMapping("/return")
+ @ApiOperation("支付之后跳转的链接")
+ public ResponseEntity returnPage(HttpServletRequest request, HttpServletResponse response) {
+ AlipayConfig alipay = alipayService.find();
+ response.setContentType("text/html;charset=" + alipay.getCharset());
+ //内容验签,防止黑客篡改参数
+ if (alipayUtils.rsaCheck(request, alipay)) {
+ //商户订单号
+ String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ //支付宝交易号
+ String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ System.out.println("商户订单号" + outTradeNo + " " + "第三方交易号" + tradeNo);
+
+ // 根据业务需要返回数据,这里统一返回OK
+ return new ResponseEntity<>("payment successful", HttpStatus.OK);
+ } else {
+ // 根据业务需要返回数据
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+ }
+
+ @ApiIgnore
+ @RequestMapping("/notify")
+ @AnonymousAccess
+ @ApiOperation("支付异步通知(要公网访问),接收异步通知,检查通知内容app_id、out_trade_no、total_amount是否与请求中的一致,根据trade_status进行后续业务处理")
+ public ResponseEntity notify(HttpServletRequest request) {
+ AlipayConfig alipay = alipayService.find();
+ Map parameterMap = request.getParameterMap();
+ //内容验签,防止黑客篡改参数
+ if (alipayUtils.rsaCheck(request, alipay)) {
+ //交易状态
+ String tradeStatus = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ // 商户订单号
+ String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ //支付宝交易号
+ String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ //付款金额
+ String totalAmount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
+ //验证
+ if (tradeStatus.equals(AliPayStatusEnum.SUCCESS.getValue()) || tradeStatus.equals(AliPayStatusEnum.FINISHED.getValue())) {
+ // 验证通过后应该根据业务需要处理订单
+ }
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/rest/EmailController.java b/studio/box/src/main/java/com/platform/rest/EmailController.java
new file mode 100644
index 0000000..123cb3c
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/rest/EmailController.java
@@ -0,0 +1,49 @@
+
+package com.platform.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import com.platform.annotation.Log;
+import com.platform.domain.vo.EmailVo;
+import com.platform.domain.EmailConfig;
+import com.platform.service.EmailService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 发送邮件
+ * @author AllDataDC
+ * @date 2023-01-27 6:55:53
+ */
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("api/email")
+@Api(tags = "工具:邮件管理")
+public class EmailController {
+
+ private final EmailService emailService;
+
+ @GetMapping
+ public ResponseEntity queryEmailConfig(){
+ return new ResponseEntity<>(emailService.find(),HttpStatus.OK);
+ }
+
+ @Log("配置邮件")
+ @PutMapping
+ @ApiOperation("配置邮件")
+ public ResponseEntity updateEmailConfig(@Validated @RequestBody EmailConfig emailConfig) throws Exception {
+ emailService.config(emailConfig,emailService.find());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @Log("发送邮件")
+ @PostMapping
+ @ApiOperation("发送邮件")
+ public ResponseEntity sendEmail(@Validated @RequestBody EmailVo emailVo){
+ emailService.send(emailVo,emailService.find());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/rest/LocalStorageController.java b/studio/box/src/main/java/com/platform/rest/LocalStorageController.java
new file mode 100644
index 0000000..d5289fc
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/rest/LocalStorageController.java
@@ -0,0 +1,84 @@
+
+package com.platform.rest;
+
+import lombok.RequiredArgsConstructor;
+import com.platform.annotation.Log;
+import com.platform.domain.LocalStorage;
+import com.platform.exception.BadRequestException;
+import com.platform.service.LocalStorageService;
+import com.platform.service.dto.LocalStorageQueryCriteria;
+import com.platform.utils.FileUtil;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import io.swagger.annotations.*;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@RestController
+@RequiredArgsConstructor
+@Api(tags = "工具:本地存储管理")
+@RequestMapping("/api/localStorage")
+public class LocalStorageController {
+
+ private final LocalStorageService localStorageService;
+
+ @GetMapping
+ @ApiOperation("查询文件")
+ @PreAuthorize("@el.check('storage:list')")
+ public ResponseEntity queryFile(LocalStorageQueryCriteria criteria, Pageable pageable){
+ return new ResponseEntity<>(localStorageService.queryAll(criteria,pageable),HttpStatus.OK);
+ }
+
+ @ApiOperation("导出数据")
+ @GetMapping(value = "/download")
+ @PreAuthorize("@el.check('storage:list')")
+ public void exportFile(HttpServletResponse response, LocalStorageQueryCriteria criteria) throws IOException {
+ localStorageService.download(localStorageService.queryAll(criteria), response);
+ }
+
+ @PostMapping
+ @ApiOperation("上传文件")
+ @PreAuthorize("@el.check('storage:add')")
+ public ResponseEntity createFile(@RequestParam String name, @RequestParam("file") MultipartFile file){
+ localStorageService.create(name, file);
+ return new ResponseEntity<>(HttpStatus.CREATED);
+ }
+
+ @ApiOperation("上传图片")
+ @PostMapping("/pictures")
+ public ResponseEntity uploadPicture(@RequestParam MultipartFile file){
+ // 判断文件是否为图片
+ String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
+ if(!FileUtil.IMAGE.equals(FileUtil.getFileType(suffix))){
+ throw new BadRequestException("只能上传图片");
+ }
+ LocalStorage localStorage = localStorageService.create(null, file);
+ return new ResponseEntity<>(localStorage, HttpStatus.OK);
+ }
+
+ @PutMapping
+ @Log("修改文件")
+ @ApiOperation("修改文件")
+ @PreAuthorize("@el.check('storage:edit')")
+ public ResponseEntity updateFile(@Validated @RequestBody LocalStorage resources){
+ localStorageService.update(resources);
+ return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+ }
+
+ @Log("删除文件")
+ @DeleteMapping
+ @ApiOperation("多选删除")
+ public ResponseEntity deleteFile(@RequestBody Long[] ids) {
+ localStorageService.deleteAll(ids);
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/rest/QiniuController.java b/studio/box/src/main/java/com/platform/rest/QiniuController.java
new file mode 100644
index 0000000..f677f1b
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/rest/QiniuController.java
@@ -0,0 +1,108 @@
+
+package com.platform.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import com.platform.annotation.Log;
+import com.platform.domain.QiniuConfig;
+import com.platform.domain.QiniuContent;
+import com.platform.service.dto.QiniuQueryCriteria;
+import com.platform.service.QiNiuService;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 发送邮件
+ * @author AllDataDC
+ * @date 2023-01-27 6:55:53
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api/qiNiuContent")
+@Api(tags = "工具:七牛云存储管理")
+public class QiniuController {
+
+ private final QiNiuService qiNiuService;
+
+ @GetMapping(value = "/config")
+ public ResponseEntity queryQiNiuConfig(){
+ return new ResponseEntity<>(qiNiuService.find(), HttpStatus.OK);
+ }
+
+ @Log("配置七牛云存储")
+ @ApiOperation("配置七牛云存储")
+ @PutMapping(value = "/config")
+ public ResponseEntity updateQiNiuConfig(@Validated @RequestBody QiniuConfig qiniuConfig){
+ qiNiuService.config(qiniuConfig);
+ qiNiuService.update(qiniuConfig.getType());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @ApiOperation("导出数据")
+ @GetMapping(value = "/download")
+ public void exportQiNiu(HttpServletResponse response, QiniuQueryCriteria criteria) throws IOException {
+ qiNiuService.downloadList(qiNiuService.queryAll(criteria), response);
+ }
+
+ @ApiOperation("查询文件")
+ @GetMapping
+ public ResponseEntity queryQiNiu(QiniuQueryCriteria criteria, Pageable pageable){
+ return new ResponseEntity<>(qiNiuService.queryAll(criteria,pageable),HttpStatus.OK);
+ }
+
+ @Log("上传文件")
+ @ApiOperation("上传文件")
+ @PostMapping
+ public ResponseEntity uploadQiNiu(@RequestParam MultipartFile file){
+ QiniuContent qiniuContent = qiNiuService.upload(file,qiNiuService.find());
+ Map map = new HashMap<>(3);
+ map.put("id",qiniuContent.getId());
+ map.put("errno",0);
+ map.put("data",new String[]{qiniuContent.getUrl()});
+ return new ResponseEntity<>(map,HttpStatus.OK);
+ }
+
+ @Log("同步七牛云数据")
+ @ApiOperation("同步七牛云数据")
+ @PostMapping(value = "/synchronize")
+ public ResponseEntity synchronizeQiNiu(){
+ qiNiuService.synchronize(qiNiuService.find());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @Log("下载文件")
+ @ApiOperation("下载文件")
+ @GetMapping(value = "/download/{id}")
+ public ResponseEntity downloadQiNiu(@PathVariable Long id){
+ Map map = new HashMap<>(1);
+ map.put("url", qiNiuService.download(qiNiuService.findByContentId(id),qiNiuService.find()));
+ return new ResponseEntity<>(map,HttpStatus.OK);
+ }
+
+ @Log("删除文件")
+ @ApiOperation("删除文件")
+ @DeleteMapping(value = "/{id}")
+ public ResponseEntity deleteQiNiu(@PathVariable Long id){
+ qiNiuService.delete(qiNiuService.findByContentId(id),qiNiuService.find());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @Log("删除多张图片")
+ @ApiOperation("删除多张图片")
+ @DeleteMapping
+ public ResponseEntity deleteAllQiNiu(@RequestBody Long[] ids) {
+ qiNiuService.deleteAll(ids, qiNiuService.find());
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/service/AliPayService.java b/studio/box/src/main/java/com/platform/service/AliPayService.java
new file mode 100644
index 0000000..f6eeba6
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/AliPayService.java
@@ -0,0 +1,43 @@
+
+package com.platform.service;
+
+import com.platform.domain.vo.TradeVo;
+import com.platform.domain.AlipayConfig;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface AliPayService {
+
+ /**
+ * 查询配置
+ * @return AlipayConfig
+ */
+ AlipayConfig find();
+
+ /**
+ * 更新配置
+ * @param alipayConfig 支付宝配置
+ * @return AlipayConfig
+ */
+ AlipayConfig config(AlipayConfig alipayConfig);
+
+ /**
+ * 处理来自PC的交易请求
+ * @param alipay 支付宝配置
+ * @param trade 交易详情
+ * @return String
+ * @throws Exception 异常
+ */
+ String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception;
+
+ /**
+ * 处理来自手机网页的交易请求
+ * @param alipay 支付宝配置
+ * @param trade 交易详情
+ * @return String
+ * @throws Exception 异常
+ */
+ String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception;
+}
diff --git a/studio/box/src/main/java/com/platform/service/EmailService.java b/studio/box/src/main/java/com/platform/service/EmailService.java
new file mode 100644
index 0000000..e749a46
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/EmailService.java
@@ -0,0 +1,35 @@
+
+package com.platform.service;
+
+import com.platform.domain.vo.EmailVo;
+import com.platform.domain.EmailConfig;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface EmailService {
+
+ /**
+ * 更新邮件配置
+ * @param emailConfig 邮箱配置
+ * @param old /
+ * @return /
+ * @throws Exception /
+ */
+ EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception;
+
+ /**
+ * 查询配置
+ * @return EmailConfig 邮件配置
+ */
+ EmailConfig find();
+
+ /**
+ * 发送邮件
+ * @param emailVo 邮件发送的内容
+ * @param emailConfig 邮件配置
+ * @throws Exception /
+ */
+ void send(EmailVo emailVo, EmailConfig emailConfig);
+}
diff --git a/studio/box/src/main/java/com/platform/service/LocalStorageService.java b/studio/box/src/main/java/com/platform/service/LocalStorageService.java
new file mode 100644
index 0000000..e0eb18f
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/LocalStorageService.java
@@ -0,0 +1,68 @@
+
+package com.platform.service;
+
+import com.platform.domain.LocalStorage;
+import com.platform.service.dto.LocalStorageDto;
+import com.platform.service.dto.LocalStorageQueryCriteria;
+import org.springframework.data.domain.Pageable;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+public interface LocalStorageService {
+
+ /**
+ * 分页查询
+ * @param criteria 条件
+ * @param pageable 分页参数
+ * @return /
+ */
+ Object queryAll(LocalStorageQueryCriteria criteria, Pageable pageable);
+
+ /**
+ * 查询全部数据
+ * @param criteria 条件
+ * @return /
+ */
+ List queryAll(LocalStorageQueryCriteria criteria);
+
+ /**
+ * 根据ID查询
+ * @param id /
+ * @return /
+ */
+ LocalStorageDto findById(Long id);
+
+ /**
+ * 上传
+ * @param name 文件名称
+ * @param file 文件
+ * @return
+ */
+ LocalStorage create(String name, MultipartFile file);
+
+ /**
+ * 编辑
+ * @param resources 文件信息
+ */
+ void update(LocalStorage resources);
+
+ /**
+ * 多选删除
+ * @param ids /
+ */
+ void deleteAll(Long[] ids);
+
+ /**
+ * 导出数据
+ * @param localStorageDtos 待导出的数据
+ * @param response /
+ * @throws IOException /
+ */
+ void download(List localStorageDtos, HttpServletResponse response) throws IOException;
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/service/QiNiuService.java b/studio/box/src/main/java/com/platform/service/QiNiuService.java
new file mode 100644
index 0000000..b025d8e
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/QiNiuService.java
@@ -0,0 +1,104 @@
+
+package com.platform.service;
+
+import com.platform.domain.QiniuConfig;
+import com.platform.domain.QiniuContent;
+import com.platform.service.dto.QiniuQueryCriteria;
+import org.springframework.data.domain.Pageable;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public interface QiNiuService {
+
+ /**
+ * 查配置
+ * @return QiniuConfig
+ */
+ QiniuConfig find();
+
+ /**
+ * 修改配置
+ * @param qiniuConfig 配置
+ * @return QiniuConfig
+ */
+ QiniuConfig config(QiniuConfig qiniuConfig);
+
+ /**
+ * 分页查询
+ * @param criteria 条件
+ * @param pageable 分页参数
+ * @return /
+ */
+ Object queryAll(QiniuQueryCriteria criteria, Pageable pageable);
+
+ /**
+ * 查询全部
+ * @param criteria 条件
+ * @return /
+ */
+ List queryAll(QiniuQueryCriteria criteria);
+
+ /**
+ * 上传文件
+ * @param file 文件
+ * @param qiniuConfig 配置
+ * @return QiniuContent
+ */
+ QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig);
+
+ /**
+ * 查询文件
+ * @param id 文件ID
+ * @return QiniuContent
+ */
+ QiniuContent findByContentId(Long id);
+
+ /**
+ * 下载文件
+ * @param content 文件信息
+ * @param config 配置
+ * @return String
+ */
+ String download(QiniuContent content, QiniuConfig config);
+
+ /**
+ * 删除文件
+ * @param content 文件
+ * @param config 配置
+ */
+ void delete(QiniuContent content, QiniuConfig config);
+
+ /**
+ * 同步数据
+ * @param config 配置
+ */
+ void synchronize(QiniuConfig config);
+
+ /**
+ * 删除文件
+ * @param ids 文件ID数组
+ * @param config 配置
+ */
+ void deleteAll(Long[] ids, QiniuConfig config);
+
+ /**
+ * 更新数据
+ * @param type 类型
+ */
+ void update(String type);
+
+ /**
+ * 导出数据
+ * @param queryAll /
+ * @param response /
+ * @throws IOException /
+ */
+ void downloadList(List queryAll, HttpServletResponse response) throws IOException;
+}
diff --git a/studio/box/src/main/java/com/platform/service/dto/LocalStorageDto.java b/studio/box/src/main/java/com/platform/service/dto/LocalStorageDto.java
new file mode 100644
index 0000000..c2fef93
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/dto/LocalStorageDto.java
@@ -0,0 +1,28 @@
+
+package com.platform.service.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import com.platform.base.BaseDTO;
+import java.io.Serializable;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Getter
+@Setter
+public class LocalStorageDto extends BaseDTO implements Serializable {
+
+ private Long id;
+
+ private String realName;
+
+ private String name;
+
+ private String suffix;
+
+ private String type;
+
+ private String size;
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/service/dto/LocalStorageQueryCriteria.java b/studio/box/src/main/java/com/platform/service/dto/LocalStorageQueryCriteria.java
new file mode 100644
index 0000000..fb0b602
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/dto/LocalStorageQueryCriteria.java
@@ -0,0 +1,22 @@
+
+package com.platform.service.dto;
+
+import lombok.Data;
+import java.sql.Timestamp;
+import java.util.List;
+
+import com.platform.annotation.Query;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Data
+public class LocalStorageQueryCriteria{
+
+ @Query(blurry = "name,suffix,type,createBy,size")
+ private String blurry;
+
+ @Query(type = Query.Type.BETWEEN)
+ private List createTime;
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/service/dto/PictureQueryCriteria.java b/studio/box/src/main/java/com/platform/service/dto/PictureQueryCriteria.java
new file mode 100644
index 0000000..57a7b5d
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/dto/PictureQueryCriteria.java
@@ -0,0 +1,26 @@
+
+package com.platform.service.dto;
+
+import lombok.Data;
+import com.platform.annotation.Query;
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * sm.ms图床
+ *
+ * @author AllDataDC
+ * @date 2023-01-27 09:52:09
+ */
+@Data
+public class PictureQueryCriteria{
+
+ @Query(type = Query.Type.INNER_LIKE)
+ private String filename;
+
+ @Query(type = Query.Type.INNER_LIKE)
+ private String username;
+
+ @Query(type = Query.Type.BETWEEN)
+ private List createTime;
+}
diff --git a/studio/box/src/main/java/com/platform/service/dto/QiniuQueryCriteria.java b/studio/box/src/main/java/com/platform/service/dto/QiniuQueryCriteria.java
new file mode 100644
index 0000000..7c3a17c
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/dto/QiniuQueryCriteria.java
@@ -0,0 +1,22 @@
+
+package com.platform.service.dto;
+
+import lombok.Data;
+import com.platform.annotation.Query;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27 09:54:37
+ */
+@Data
+public class QiniuQueryCriteria{
+
+ @Query(type = Query.Type.INNER_LIKE)
+ private String key;
+
+ @Query(type = Query.Type.BETWEEN)
+ private List createTime;
+}
diff --git a/studio/box/src/main/java/com/platform/service/impl/AliPayServiceImpl.java b/studio/box/src/main/java/com/platform/service/impl/AliPayServiceImpl.java
new file mode 100644
index 0000000..160ab7b
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/impl/AliPayServiceImpl.java
@@ -0,0 +1,105 @@
+
+package com.platform.service.impl;
+
+import com.alipay.api.AlipayClient;
+import com.alipay.api.DefaultAlipayClient;
+import com.alipay.api.request.AlipayTradePagePayRequest;
+import com.alipay.api.request.AlipayTradeWapPayRequest;
+import lombok.RequiredArgsConstructor;
+import com.platform.domain.vo.TradeVo;
+import com.platform.domain.AlipayConfig;
+import com.platform.exception.BadRequestException;
+import com.platform.repository.AliPayRepository;
+import com.platform.service.AliPayService;
+import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.Optional;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Service
+@RequiredArgsConstructor
+@CacheConfig(cacheNames = "aliPay")
+public class AliPayServiceImpl implements AliPayService {
+
+ private final AliPayRepository alipayRepository;
+
+ @Override
+ @Cacheable(key = "'config'")
+ public AlipayConfig find() {
+ Optional alipayConfig = alipayRepository.findById(1L);
+ return alipayConfig.orElseGet(AlipayConfig::new);
+ }
+
+ @Override
+ @CachePut(key = "'config'")
+ @Transactional(rollbackFor = Exception.class)
+ public AlipayConfig config(AlipayConfig alipayConfig) {
+ alipayConfig.setId(1L);
+ return alipayRepository.save(alipayConfig);
+ }
+
+ @Override
+ public String toPayAsPc(AlipayConfig alipay, TradeVo trade) throws Exception {
+
+ if(alipay.getId() == null){
+ throw new BadRequestException("请先添加相应配置,再操作");
+ }
+ AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType());
+
+ // 创建API对应的request(电脑网页版)
+ AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
+
+ // 订单完成后返回的页面和异步通知地址
+ request.setReturnUrl(alipay.getReturnUrl());
+ request.setNotifyUrl(alipay.getNotifyUrl());
+ // 填充订单参数
+ request.setBizContent("{" +
+ " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," +
+ " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
+ " \"total_amount\":"+trade.getTotalAmount()+"," +
+ " \"subject\":\""+trade.getSubject()+"\"," +
+ " \"body\":\""+trade.getBody()+"\"," +
+ " \"extend_params\":{" +
+ " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" +
+ " }"+
+ " }");//填充业务参数
+ // 调用SDK生成表单, 通过GET方式,口可以获取url
+ return alipayClient.pageExecute(request, "GET").getBody();
+
+ }
+
+ @Override
+ public String toPayAsWeb(AlipayConfig alipay, TradeVo trade) throws Exception {
+ if(alipay.getId() == null){
+ throw new BadRequestException("请先添加相应配置,再操作");
+ }
+ AlipayClient alipayClient = new DefaultAlipayClient(alipay.getGatewayUrl(), alipay.getAppId(), alipay.getPrivateKey(), alipay.getFormat(), alipay.getCharset(), alipay.getPublicKey(), alipay.getSignType());
+
+ double money = Double.parseDouble(trade.getTotalAmount());
+ double maxMoney = 5000;
+ if(money <= 0 || money >= maxMoney){
+ throw new BadRequestException("测试金额过大");
+ }
+ // 创建API对应的request(手机网页版)
+ AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
+ request.setReturnUrl(alipay.getReturnUrl());
+ request.setNotifyUrl(alipay.getNotifyUrl());
+ request.setBizContent("{" +
+ " \"out_trade_no\":\""+trade.getOutTradeNo()+"\"," +
+ " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
+ " \"total_amount\":"+trade.getTotalAmount()+"," +
+ " \"subject\":\""+trade.getSubject()+"\"," +
+ " \"body\":\""+trade.getBody()+"\"," +
+ " \"extend_params\":{" +
+ " \"sys_service_provider_id\":\""+alipay.getSysServiceProviderId()+"\"" +
+ " }"+
+ " }");
+ return alipayClient.pageExecute(request, "GET").getBody();
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/service/impl/EmailServiceImpl.java b/studio/box/src/main/java/com/platform/service/impl/EmailServiceImpl.java
new file mode 100644
index 0000000..a68835e
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/impl/EmailServiceImpl.java
@@ -0,0 +1,91 @@
+
+package com.platform.service.impl;
+
+import cn.hutool.extra.mail.Mail;
+import cn.hutool.extra.mail.MailAccount;
+import lombok.RequiredArgsConstructor;
+import com.platform.domain.EmailConfig;
+import com.platform.domain.vo.EmailVo;
+import com.platform.exception.BadRequestException;
+import com.platform.repository.EmailRepository;
+import com.platform.service.EmailService;
+import com.platform.utils.EncryptUtils;
+import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.util.Optional;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Service
+@RequiredArgsConstructor
+@CacheConfig(cacheNames = "email")
+public class EmailServiceImpl implements EmailService {
+
+ private final EmailRepository emailRepository;
+
+ @Override
+ @CachePut(key = "'config'")
+ @Transactional(rollbackFor = Exception.class)
+ public EmailConfig config(EmailConfig emailConfig, EmailConfig old) throws Exception {
+ emailConfig.setId(1L);
+ if(!emailConfig.getPass().equals(old.getPass())){
+ // 对称加密
+ emailConfig.setPass(EncryptUtils.desEncrypt(emailConfig.getPass()));
+ }
+ return emailRepository.save(emailConfig);
+ }
+
+ @Override
+ @Cacheable(key = "'config'")
+ public EmailConfig find() {
+ Optional emailConfig = emailRepository.findById(1L);
+ return emailConfig.orElseGet(EmailConfig::new);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void send(EmailVo emailVo, EmailConfig emailConfig){
+ if(emailConfig.getId() == null){
+ throw new BadRequestException("请先配置,再操作");
+ }
+ // 封装
+ MailAccount account = new MailAccount();
+ // 设置用户
+ String user = emailConfig.getFromUser().split("@")[0];
+ account.setUser(user);
+ account.setHost(emailConfig.getHost());
+ account.setPort(Integer.parseInt(emailConfig.getPort()));
+ account.setAuth(true);
+ try {
+ // 对称解密
+ account.setPass(EncryptUtils.desDecrypt(emailConfig.getPass()));
+ } catch (Exception e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ account.setFrom(emailConfig.getUser()+"<"+emailConfig.getFromUser()+">");
+ // ssl方式发送
+ account.setSslEnable(true);
+ // 使用STARTTLS安全连接
+ account.setStarttlsEnable(true);
+ String content = emailVo.getContent();
+ // 发送
+ try {
+ int size = emailVo.getTos().size();
+ Mail.create(account)
+ .setTos(emailVo.getTos().toArray(new String[size]))
+ .setTitle(emailVo.getSubject())
+ .setContent(content)
+ .setHtml(true)
+ //关闭session
+ .setUseGlobalSession(false)
+ .send();
+ }catch (Exception e){
+ throw new BadRequestException(e.getMessage());
+ }
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/service/impl/LocalStorageServiceImpl.java b/studio/box/src/main/java/com/platform/service/impl/LocalStorageServiceImpl.java
new file mode 100644
index 0000000..cf5e0cf
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/impl/LocalStorageServiceImpl.java
@@ -0,0 +1,119 @@
+
+package com.platform.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.RequiredArgsConstructor;
+import com.platform.config.FileProperties;
+import com.platform.domain.LocalStorage;
+import com.platform.service.dto.LocalStorageDto;
+import com.platform.service.dto.LocalStorageQueryCriteria;
+import com.platform.service.mapstruct.LocalStorageMapper;
+import com.platform.exception.BadRequestException;
+import com.platform.utils.*;
+import com.platform.repository.LocalStorageRepository;
+import com.platform.service.LocalStorageService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Service
+@RequiredArgsConstructor
+public class LocalStorageServiceImpl implements LocalStorageService {
+
+ private final LocalStorageRepository localStorageRepository;
+ private final LocalStorageMapper localStorageMapper;
+ private final FileProperties properties;
+
+ @Override
+ public Object queryAll(LocalStorageQueryCriteria criteria, Pageable pageable){
+ Page page = localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
+ return PageUtil.toPage(page.map(localStorageMapper::toDto));
+ }
+
+ @Override
+ public List queryAll(LocalStorageQueryCriteria criteria){
+ return localStorageMapper.toDto(localStorageRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
+ }
+
+ @Override
+ public LocalStorageDto findById(Long id){
+ LocalStorage localStorage = localStorageRepository.findById(id).orElseGet(LocalStorage::new);
+ ValidationUtil.isNull(localStorage.getId(),"LocalStorage","id",id);
+ return localStorageMapper.toDto(localStorage);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public LocalStorage create(String name, MultipartFile multipartFile) {
+ FileUtil.checkSize(properties.getMaxSize(), multipartFile.getSize());
+ String suffix = FileUtil.getExtensionName(multipartFile.getOriginalFilename());
+ String type = FileUtil.getFileType(suffix);
+ File file = FileUtil.upload(multipartFile, properties.getPath().getPath() + type + File.separator);
+ if(ObjectUtil.isNull(file)){
+ throw new BadRequestException("上传失败");
+ }
+ try {
+ name = StringUtils.isBlank(name) ? FileUtil.getFileNameNoEx(multipartFile.getOriginalFilename()) : name;
+ LocalStorage localStorage = new LocalStorage(
+ file.getName(),
+ name,
+ suffix,
+ file.getPath(),
+ type,
+ FileUtil.getSize(file.length())
+ );
+ return localStorageRepository.save(localStorage);
+ }catch (Exception e){
+ FileUtil.del(file);
+ throw e;
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(LocalStorage resources) {
+ LocalStorage localStorage = localStorageRepository.findById(resources.getId()).orElseGet(LocalStorage::new);
+ ValidationUtil.isNull( localStorage.getId(),"LocalStorage","id",resources.getId());
+ localStorage.copy(resources);
+ localStorageRepository.save(localStorage);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteAll(Long[] ids) {
+ for (Long id : ids) {
+ LocalStorage storage = localStorageRepository.findById(id).orElseGet(LocalStorage::new);
+ FileUtil.del(storage.getPath());
+ localStorageRepository.delete(storage);
+ }
+ }
+
+ @Override
+ public void download(List queryAll, HttpServletResponse response) throws IOException {
+ List> list = new ArrayList<>();
+ for (LocalStorageDto localStorageDTO : queryAll) {
+ Map map = new LinkedHashMap<>();
+ map.put("文件名", localStorageDTO.getRealName());
+ map.put("备注名", localStorageDTO.getName());
+ map.put("文件类型", localStorageDTO.getType());
+ map.put("文件大小", localStorageDTO.getSize());
+ map.put("创建者", localStorageDTO.getCreateBy());
+ map.put("创建日期", localStorageDTO.getCreateTime());
+ list.add(map);
+ }
+ FileUtil.downloadExcel(list, response);
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/service/impl/QiNiuServiceImpl.java b/studio/box/src/main/java/com/platform/service/impl/QiNiuServiceImpl.java
new file mode 100644
index 0000000..47178e5
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/impl/QiNiuServiceImpl.java
@@ -0,0 +1,223 @@
+
+package com.platform.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.qiniu.common.QiniuException;
+import com.qiniu.http.Response;
+import com.qiniu.storage.BucketManager;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.storage.model.DefaultPutRet;
+import com.qiniu.storage.model.FileInfo;
+import com.qiniu.util.Auth;
+import lombok.RequiredArgsConstructor;
+import com.platform.domain.QiniuConfig;
+import com.platform.domain.QiniuContent;
+import com.platform.repository.QiniuContentRepository;
+import com.platform.service.dto.QiniuQueryCriteria;
+import com.platform.utils.QiNiuUtil;
+import com.platform.exception.BadRequestException;
+import com.platform.repository.QiNiuConfigRepository;
+import com.platform.service.QiNiuService;
+import com.platform.utils.FileUtil;
+import com.platform.utils.PageUtil;
+import com.platform.utils.QueryHelp;
+import com.platform.utils.ValidationUtil;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.CacheConfig;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Service
+@RequiredArgsConstructor
+@CacheConfig(cacheNames = "qiNiu")
+public class QiNiuServiceImpl implements QiNiuService {
+
+ private final QiNiuConfigRepository qiNiuConfigRepository;
+ private final QiniuContentRepository qiniuContentRepository;
+
+ @Value("${qiniu.max-size}")
+ private Long maxSize;
+
+ @Override
+ @Cacheable(key = "'config'")
+ public QiniuConfig find() {
+ Optional qiniuConfig = qiNiuConfigRepository.findById(1L);
+ return qiniuConfig.orElseGet(QiniuConfig::new);
+ }
+
+ @Override
+ @CachePut(key = "'config'")
+ @Transactional(rollbackFor = Exception.class)
+ public QiniuConfig config(QiniuConfig qiniuConfig) {
+ qiniuConfig.setId(1L);
+ String http = "http://", https = "https://";
+ if (!(qiniuConfig.getHost().toLowerCase().startsWith(http)||qiniuConfig.getHost().toLowerCase().startsWith(https))) {
+ throw new BadRequestException("外链域名必须以http://或者https://开头");
+ }
+ return qiNiuConfigRepository.save(qiniuConfig);
+ }
+
+ @Override
+ public Object queryAll(QiniuQueryCriteria criteria, Pageable pageable){
+ return PageUtil.toPage(qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable));
+ }
+
+ @Override
+ public List queryAll(QiniuQueryCriteria criteria) {
+ return qiniuContentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder));
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public QiniuContent upload(MultipartFile file, QiniuConfig qiniuConfig) {
+ FileUtil.checkSize(maxSize, file.getSize());
+ if(qiniuConfig.getId() == null){
+ throw new BadRequestException("请先添加相应配置,再操作");
+ }
+ // 构造一个带指定Zone对象的配置类
+ Configuration cfg = new Configuration(QiNiuUtil.getRegion(qiniuConfig.getZone()));
+ UploadManager uploadManager = new UploadManager(cfg);
+ Auth auth = Auth.create(qiniuConfig.getAccessKey(), qiniuConfig.getSecretKey());
+ String upToken = auth.uploadToken(qiniuConfig.getBucket());
+ try {
+ String key = file.getOriginalFilename();
+ if(qiniuContentRepository.findByKey(key) != null) {
+ key = QiNiuUtil.getKey(key);
+ }
+ Response response = uploadManager.put(file.getBytes(), key, upToken);
+ //解析上传成功的结果
+
+ DefaultPutRet putRet = JSON.parseObject(response.bodyString(), DefaultPutRet.class);
+ QiniuContent content = qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(putRet.key));
+ if(content == null){
+ //存入数据库
+ QiniuContent qiniuContent = new QiniuContent();
+ qiniuContent.setSuffix(FileUtil.getExtensionName(putRet.key));
+ qiniuContent.setBucket(qiniuConfig.getBucket());
+ qiniuContent.setType(qiniuConfig.getType());
+ qiniuContent.setKey(FileUtil.getFileNameNoEx(putRet.key));
+ qiniuContent.setUrl(qiniuConfig.getHost()+"/"+putRet.key);
+ qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(file.getSize()+"")));
+ return qiniuContentRepository.save(qiniuContent);
+ }
+ return content;
+ } catch (Exception e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ }
+
+ @Override
+ public QiniuContent findByContentId(Long id) {
+ QiniuContent qiniuContent = qiniuContentRepository.findById(id).orElseGet(QiniuContent::new);
+ ValidationUtil.isNull(qiniuContent.getId(),"QiniuContent", "id",id);
+ return qiniuContent;
+ }
+
+ @Override
+ public String download(QiniuContent content,QiniuConfig config){
+ String finalUrl;
+ String type = "公开";
+ if(type.equals(content.getType())){
+ finalUrl = content.getUrl();
+ } else {
+ Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
+ // 1小时,可以自定义链接过期时间
+ long expireInSeconds = 3600;
+ finalUrl = auth.privateDownloadUrl(content.getUrl(), expireInSeconds);
+ }
+ return finalUrl;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void delete(QiniuContent content, QiniuConfig config) {
+ //构造一个带指定Zone对象的配置类
+ Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone()));
+ Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
+ BucketManager bucketManager = new BucketManager(auth, cfg);
+ try {
+ bucketManager.delete(content.getBucket(), content.getKey() + "." + content.getSuffix());
+ qiniuContentRepository.delete(content);
+ } catch (QiniuException ex) {
+ qiniuContentRepository.delete(content);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void synchronize(QiniuConfig config) {
+ if(config.getId() == null){
+ throw new BadRequestException("请先添加相应配置,再操作");
+ }
+ //构造一个带指定Zone对象的配置类
+ Configuration cfg = new Configuration(QiNiuUtil.getRegion(config.getZone()));
+ Auth auth = Auth.create(config.getAccessKey(), config.getSecretKey());
+ BucketManager bucketManager = new BucketManager(auth, cfg);
+ //文件名前缀
+ String prefix = "";
+ //每次迭代的长度限制,最大1000,推荐值 1000
+ int limit = 1000;
+ //指定目录分隔符,列出所有公共前缀(模拟列出目录效果)。缺省值为空字符串
+ String delimiter = "";
+ //列举空间文件列表
+ BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(config.getBucket(), prefix, limit, delimiter);
+ while (fileListIterator.hasNext()) {
+ //处理获取的file list结果
+ QiniuContent qiniuContent;
+ FileInfo[] items = fileListIterator.next();
+ for (FileInfo item : items) {
+ if(qiniuContentRepository.findByKey(FileUtil.getFileNameNoEx(item.key)) == null){
+ qiniuContent = new QiniuContent();
+ qiniuContent.setSize(FileUtil.getSize(Integer.parseInt(item.fsize+"")));
+ qiniuContent.setSuffix(FileUtil.getExtensionName(item.key));
+ qiniuContent.setKey(FileUtil.getFileNameNoEx(item.key));
+ qiniuContent.setType(config.getType());
+ qiniuContent.setBucket(config.getBucket());
+ qiniuContent.setUrl(config.getHost()+"/"+item.key);
+ qiniuContentRepository.save(qiniuContent);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void deleteAll(Long[] ids, QiniuConfig config) {
+ for (Long id : ids) {
+ delete(findByContentId(id), config);
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(String type) {
+ qiNiuConfigRepository.update(type);
+ }
+
+ @Override
+ public void downloadList(List queryAll, HttpServletResponse response) throws IOException {
+ List> list = new ArrayList<>();
+ for (QiniuContent content : queryAll) {
+ Map map = new LinkedHashMap<>();
+ map.put("文件名", content.getKey());
+ map.put("文件类型", content.getSuffix());
+ map.put("空间名称", content.getBucket());
+ map.put("文件大小", content.getSize());
+ map.put("空间类型", content.getType());
+ map.put("创建日期", content.getUpdateTime());
+ list.add(map);
+ }
+ FileUtil.downloadExcel(list, response);
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/service/mapstruct/LocalStorageMapper.java b/studio/box/src/main/java/com/platform/service/mapstruct/LocalStorageMapper.java
new file mode 100644
index 0000000..c2710cd
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/service/mapstruct/LocalStorageMapper.java
@@ -0,0 +1,17 @@
+
+package com.platform.service.mapstruct;
+
+import com.platform.base.BaseMapper;
+import com.platform.service.dto.LocalStorageDto;
+import com.platform.domain.LocalStorage;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface LocalStorageMapper extends BaseMapper {
+
+}
\ No newline at end of file
diff --git a/studio/box/src/main/java/com/platform/utils/AliPayStatusEnum.java b/studio/box/src/main/java/com/platform/utils/AliPayStatusEnum.java
new file mode 100644
index 0000000..36e2baf
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/utils/AliPayStatusEnum.java
@@ -0,0 +1,32 @@
+
+package com.platform.utils;
+
+/**
+ * 支付状态
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public enum AliPayStatusEnum {
+
+ /** 交易成功 */
+ FINISHED("TRADE_FINISHED"),
+
+ /** 支付成功 */
+ SUCCESS("TRADE_SUCCESS"),
+
+ /** 交易创建 */
+ BUYER_PAY("WAIT_BUYER_PAY"),
+
+ /** 交易关闭 */
+ CLOSED("TRADE_CLOSED");
+
+ private final String value;
+
+ AliPayStatusEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/utils/AlipayUtils.java b/studio/box/src/main/java/com/platform/utils/AlipayUtils.java
new file mode 100644
index 0000000..3cc2242
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/utils/AlipayUtils.java
@@ -0,0 +1,71 @@
+
+package com.platform.utils;
+
+import com.alipay.api.AlipayApiException;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.platform.domain.AlipayConfig;
+import org.springframework.stereotype.Component;
+import javax.servlet.http.HttpServletRequest;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 支付宝工具类
+ * @author AllDataDC
+ * @date 2023-01-27 14:04:35
+ */
+@Component
+public class AlipayUtils {
+
+ /**
+ * 生成订单号
+ * @return String
+ */
+ public String getOrderCode() {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ int a = (int)(Math.random() * 9000.0D) + 1000;
+ System.out.println(a);
+ Date date = new Date();
+ String str = sdf.format(date);
+ String[] split = str.split("-");
+ String s = split[0] + split[1] + split[2];
+ String[] split1 = s.split(" ");
+ String s1 = split1[0] + split1[1];
+ String[] split2 = s1.split(":");
+ return split2[0] + split2[1] + split2[2] + a;
+ }
+
+ /**
+ * 校验签名
+ * @param request HttpServletRequest
+ * @param alipay 阿里云配置
+ * @return boolean
+ */
+ public boolean rsaCheck(HttpServletRequest request, AlipayConfig alipay){
+
+ // 获取支付宝POST过来反馈信息
+ Map params = new HashMap<>(1);
+ Map requestParams = request.getParameterMap();
+ for (Object o : requestParams.keySet()) {
+ String name = (String) o;
+ String[] values = requestParams.get(name);
+ String valueStr = "";
+ for (int i = 0; i < values.length; i++) {
+ valueStr = (i == values.length - 1) ? valueStr + values[i]
+ : valueStr + values[i] + ",";
+ }
+ params.put(name, valueStr);
+ }
+
+ try {
+ return AlipaySignature.rsaCheckV1(params,
+ alipay.getPublicKey(),
+ alipay.getCharset(),
+ alipay.getSignType());
+ } catch (AlipayApiException e) {
+ return false;
+ }
+ }
+}
diff --git a/studio/box/src/main/java/com/platform/utils/QiNiuUtil.java b/studio/box/src/main/java/com/platform/utils/QiNiuUtil.java
new file mode 100644
index 0000000..d231764
--- /dev/null
+++ b/studio/box/src/main/java/com/platform/utils/QiNiuUtil.java
@@ -0,0 +1,57 @@
+
+package com.platform.utils;
+
+import com.qiniu.storage.Region;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 七牛云存储工具类
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public class QiNiuUtil {
+
+ private static final String HUAD = "华东";
+
+ private static final String HUAB = "华北";
+
+ private static final String HUAN = "华南";
+
+ private static final String BEIM = "北美";
+
+ /**
+ * 得到机房的对应关系
+ * @param zone 机房名称
+ * @return Region
+ */
+ public static Region getRegion(String zone){
+
+ if(HUAD.equals(zone)){
+ return Region.huadong();
+ } else if(HUAB.equals(zone)){
+ return Region.huabei();
+ } else if(HUAN.equals(zone)){
+ return Region.huanan();
+ } else if (BEIM.equals(zone)){
+ return Region.beimei();
+ // 否则就是东南亚
+ } else {
+ return Region.qvmHuadong();
+ }
+ }
+
+ /**
+ * 默认不指定key的情况下,以文件内容的hash值作为文件名
+ * @param file 文件名
+ * @return String
+ */
+ public static String getKey(String file){
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+ Date date = new Date();
+ return FileUtil.getFileNameNoEx(file) + "-" +
+ sdf.format(date) +
+ "." +
+ FileUtil.getExtensionName(file);
+ }
+}
diff --git a/studio/common/aspose-words-20.3.jar b/studio/common/aspose-words-20.3.jar
new file mode 100644
index 0000000..4db4eb8
Binary files /dev/null and b/studio/common/aspose-words-20.3.jar differ
diff --git a/studio/common/common-core/pom.xml b/studio/common/common-core/pom.xml
new file mode 100644
index 0000000..d8aa8df
--- /dev/null
+++ b/studio/common/common-core/pom.xml
@@ -0,0 +1,115 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-core
+
+
+
+
+ org.springframework
+ spring-webmvc
+ provided
+
+
+
+ commons-io
+ commons-io
+ ${commons.io.version}
+
+
+
+ commons-fileupload
+ commons-fileupload
+ ${commons.fileupload.version}
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ commons-codec
+ commons-codec
+
+
+ commons-beanutils
+ commons-beanutils
+ ${commons.beanutils.version}
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+ com.baomidou
+ mybatis-plus-extension
+ ${mybatis-plus.version}
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+ com.fasterxml.jackson.module
+ jackson-module-parameter-names
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jdk8
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+ org.springframework.security
+ spring-security-core
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ ${jjwt.version}
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ ${jjwt.version}
+
+
+ com.platform
+ common-service-api
+ 0.4.x
+ compile
+
+
+ com.platform
+ common-log
+ 0.4.x
+ compile
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/BaseController.java b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseController.java
new file mode 100644
index 0000000..bb4c58f
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseController.java
@@ -0,0 +1,5 @@
+package cn.datax.common.base;
+
+public abstract class BaseController {
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/BaseDao.java b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseDao.java
new file mode 100644
index 0000000..d30b2e7
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseDao.java
@@ -0,0 +1,15 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BaseDao extends BaseMapper {
+
+ List selectListDataScope(@Param("ew") Wrapper queryWrapper, @Param("dataScope") DataScope dataScope);
+
+ IPage selectPageDataScope(IPage page, @Param("ew") Wrapper queryWrapper, @Param("dataScope") DataScope dataScope);
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/BaseService.java b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseService.java
new file mode 100644
index 0000000..1d585f2
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseService.java
@@ -0,0 +1,7 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+
+public interface BaseService extends IService {
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/BaseServiceImpl.java b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseServiceImpl.java
new file mode 100644
index 0000000..32e2e4c
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/BaseServiceImpl.java
@@ -0,0 +1,10 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public abstract class BaseServiceImpl, T> extends ServiceImpl implements BaseService {
+
+ @Autowired
+ protected M baseDao;
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/DataFlowBaseEntity.java b/studio/common/common-core/src/main/java/cn/datax/common/base/DataFlowBaseEntity.java
new file mode 100644
index 0000000..5a45a38
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/DataFlowBaseEntity.java
@@ -0,0 +1,25 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public abstract class DataFlowBaseEntity extends DataScopeBaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 工作流状态(1待提交,2已退回,3审核中,4通过,5不通过,6已撤销)
+ */
+ @TableField(value = "flow_status", fill = FieldFill.INSERT)
+ private String flowStatus;
+
+ /**
+ * 流程实例ID
+ */
+ @TableField(value = "process_instance_id")
+ private String processInstanceId;
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/DataScope.java b/studio/common/common-core/src/main/java/cn/datax/common/base/DataScope.java
new file mode 100644
index 0000000..de71417
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/DataScope.java
@@ -0,0 +1,24 @@
+package cn.datax.common.base;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 数据权限查询参数
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class DataScope {
+
+ /**
+ * 表的部门字段
+ */
+ private String deptScopeName = "create_dept";
+
+ /**
+ * 表的用户字段
+ */
+ private String userScopeName = "create_by";
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/base/DataScopeBaseEntity.java b/studio/common/common-core/src/main/java/cn/datax/common/base/DataScopeBaseEntity.java
new file mode 100644
index 0000000..9e01153
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/base/DataScopeBaseEntity.java
@@ -0,0 +1,19 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public abstract class DataScopeBaseEntity extends BaseEntity {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 创建人所属部门
+ */
+ @TableField(value = "create_dept", fill = FieldFill.INSERT)
+ private String createDept;
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/config/JacksonConfig.java b/studio/common/common-core/src/main/java/cn/datax/common/config/JacksonConfig.java
new file mode 100644
index 0000000..8e411b6
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/config/JacksonConfig.java
@@ -0,0 +1,75 @@
+package cn.datax.common.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.TimeZone;
+
+@Configuration
+public class JacksonConfig {
+
+ @Bean
+ @Primary
+ @ConditionalOnMissingBean(ObjectMapper.class)
+ public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder){
+ builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ ObjectMapper objectMapper = builder.createXmlMapper(false)
+ .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
+ .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
+ .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
+ .build();
+ // null数据不返回
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+ // 反序列化时候遇到不匹配的属性并不抛出异常
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ // 序列化时候遇到空对象不抛出异常
+ objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ // 反序列化的时候如果是无效子类型,不抛出异常
+ objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
+ // 不使用默认的dateTime进行序列化,
+ objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
+ // 数据精度问题
+ SimpleModule simpleModule = new SimpleModule()
+ .addSerializer(Long.class, ToStringSerializer.instance)
+ .addSerializer(Long.TYPE, ToStringSerializer.instance)
+ .addSerializer(BigInteger.class, ToStringSerializer.instance)
+ .addSerializer(BigDecimal.class, ToStringSerializer.instance);
+ objectMapper.registerModule(simpleModule);
+
+ JavaTimeModule javaTimeModule = new JavaTimeModule();
+ javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+ javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+ javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
+ javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+ javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+ javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
+ objectMapper.registerModule(javaTimeModule).registerModule(new ParameterNamesModule());
+ return objectMapper;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/config/RestTemplateConfig.java b/studio/common/common-core/src/main/java/cn/datax/common/config/RestTemplateConfig.java
new file mode 100644
index 0000000..5843c6d
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/config/RestTemplateConfig.java
@@ -0,0 +1,24 @@
+package cn.datax.common.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.SimpleClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ public RestTemplate restTemplate(ClientHttpRequestFactory factory){
+ return new RestTemplate(factory);
+ }
+
+ @Bean
+ public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
+ SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
+ factory.setReadTimeout(5000);
+ factory.setConnectTimeout(5000);
+ return factory;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/DataConstant.java b/studio/common/common-core/src/main/java/cn/datax/common/core/DataConstant.java
new file mode 100644
index 0000000..58af1be
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/DataConstant.java
@@ -0,0 +1,211 @@
+package cn.datax.common.core;
+
+public class DataConstant {
+
+ /**
+ * Oauth2安全相关常量
+ */
+ public enum Security {
+ //请求头TOKEN名称
+ TOKENHEADER("gatewayToken"),
+ //请求头TOKEN值
+ TOKENVALUE("datax:gateway:123456"),
+ //OAUTH2请求头
+ AUTHORIZATION("Authorization"),
+ //OAUTH2令牌类型
+ TOKENTYPE("bearer "),
+ //security授权角色前缀
+ ROLEPREFIX("ROLE_");
+
+ Security(String val){
+ this.val = val;
+ }
+
+ private final String val;
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 通用的是否
+ */
+ public enum TrueOrFalse {
+ FALSE("0",false),
+ TRUE("1",true);
+
+ TrueOrFalse(String key, boolean val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final boolean val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public boolean getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 用户认证返回额外信息
+ */
+ public enum UserAdditionalInfo {
+ LICENSE("license", "datax"),
+ USER("user", "用户"),
+ USERID("user_id", "用户ID"),
+ USERNAME("username", "用户名"),
+ NICKNAME("nickname", "用户昵称"),
+ DEPT("user_dept", "用户部门"),
+ ROLE("user_role", "用户角色"),
+ POST("user_post", "用户岗位");
+
+ UserAdditionalInfo(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 通用的启用禁用状态
+ */
+ public enum EnableState {
+ DISABLE("0","禁用"),
+ ENABLE("1","启用");
+
+ EnableState(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 流程审核状态
+ */
+ public enum AuditState{
+ WAIT("1","待提交"),
+ BACK("2", "已退回"),
+ AUDIT("3","审核中"),
+ AGREE("4","通过"),
+ REJECT("5","不通过"),
+ CANCEL("6", "已撤销");
+
+ AuditState(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 菜单类型
+ */
+ public enum MenuType{
+ MODULE("0","模块"),
+ MENU("1","菜单"),
+ BUTTON("2","按钮");
+
+ MenuType(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * 数据范围
+ */
+ public enum DataScope{
+ ALL("1","全部数据权限"),
+ CUSTOM("2","自定义数据权限"),
+ DEPT("3","本部门数据权限"),
+ DEPTANDCHILD("4","本部门及以下数据权限"),
+ SELF("5","仅本人数据权限");
+ DataScope(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+
+ /**
+ * Api状态
+ */
+ public enum ApiState{
+ WAIT("1","待发布"),
+ RELEASE("2","已发布"),
+ CANCEL("3","已下线");
+ ApiState(String key, String val){
+ this.key = key;
+ this.val = val;
+ }
+
+ private final String key;
+ private final String val;
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getVal() {
+ return val;
+ }
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/DataRole.java b/studio/common/common-core/src/main/java/cn/datax/common/core/DataRole.java
new file mode 100644
index 0000000..506130f
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/DataRole.java
@@ -0,0 +1,14 @@
+package cn.datax.common.core;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DataRole implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String dataScope;
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/DataUser.java b/studio/common/common-core/src/main/java/cn/datax/common/core/DataUser.java
new file mode 100644
index 0000000..6136606
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/DataUser.java
@@ -0,0 +1,85 @@
+package cn.datax.common.core;
+
+import cn.hutool.core.util.ObjectUtil;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Collection;
+import java.util.List;
+
+@EqualsAndHashCode(callSuper = true)
+public class DataUser extends User {
+
+ private String id;
+ private String nickname;
+ private String dept;
+ private List roles;
+ private List posts;
+
+ public DataUser(String id, String nickname, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection extends GrantedAuthority> authorities) {
+ super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+ this.id = id;
+ this.nickname = nickname;
+ }
+
+ public boolean isAdmin() {
+ return isAdmin(this.getUsername());
+ }
+
+ public static boolean isAdmin(String username) {
+ return ObjectUtil.equal(username, "admin");
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public void setNickname(String nickname) {
+ this.nickname = nickname;
+ }
+
+ public String getDept() {
+ return dept;
+ }
+
+ public void setDept(String dept) {
+ this.dept = dept;
+ }
+
+ public List getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+ public List getPosts() {
+ return posts;
+ }
+
+ public void setPosts(List posts) {
+ this.posts = posts;
+ }
+
+ @Override
+ public String toString() {
+ return "DataUser{" +
+ "id='" + id + '\'' +
+ ", nickname='" + nickname + '\'' +
+ ", dept='" + dept + '\'' +
+ ", roles=" + roles +
+ ", posts=" + posts +
+ '}';
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/JsonPage.java b/studio/common/common-core/src/main/java/cn/datax/common/core/JsonPage.java
new file mode 100644
index 0000000..3c6ff10
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/JsonPage.java
@@ -0,0 +1,26 @@
+package cn.datax.common.core;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class JsonPage implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private Integer pageNum;
+ private Integer pageSize;
+ private Integer total;
+ private List data;
+
+ public JsonPage(Long pageNum, Long pageSize, Long total, List data) {
+ this.pageNum = pageNum.intValue();
+ this.pageSize = pageSize.intValue();
+ this.total = total.intValue();
+ this.data = data;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/R.java b/studio/common/common-core/src/main/java/cn/datax/common/core/R.java
new file mode 100644
index 0000000..a4f23bd
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/R.java
@@ -0,0 +1,88 @@
+package cn.datax.common.core;
+
+import lombok.Getter;
+
+import java.io.Serializable;
+
+@Getter
+public class R implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private boolean success;
+ private int code;
+ private String msg;
+ private Object data;
+ private long timestamp;
+
+ private R() {}
+
+ public static R error() {
+ return error(null);
+ }
+
+ public static R error(String message) {
+ return error(null, message);
+ }
+
+ public static R error(Integer code, String message) {
+ if(code == null) {
+ code = 500;
+ }
+ if(message == null) {
+ message = "服务器内部错误";
+ }
+ return build(code, false, message);
+ }
+
+ public static R ok() {
+ return ok(null);
+ }
+
+ public static R ok(String message) {
+ return ok(null, message);
+ }
+
+ public static R ok(Integer code, String message) {
+ if(code == null) {
+ code = 200;
+ }
+ if(message == null) {
+ message = "操作成功";
+ }
+ return build(code, true, message);
+ }
+
+ public static R build(int code, boolean success, String message) {
+ return new R()
+ .setCode(code)
+ .setSuccess(success)
+ .setMessage(message)
+ .setTimestamp(System.currentTimeMillis());
+ }
+
+ public R setCode(int code) {
+ this.code = code;
+ return this;
+ }
+
+ public R setSuccess(boolean success) {
+ this.success = success;
+ return this;
+ }
+ public R setMessage(String msg) {
+ this.msg = msg;
+ return this;
+ }
+
+ public R setData(Object data) {
+ this.data = data;
+ return this;
+ }
+
+ public R setTimestamp(long timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/core/RedisConstant.java b/studio/common/common-core/src/main/java/cn/datax/common/core/RedisConstant.java
new file mode 100644
index 0000000..35d2d9b
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/core/RedisConstant.java
@@ -0,0 +1,26 @@
+package cn.datax.common.core;
+
+public interface RedisConstant {
+
+ String MARKET_API_KEY = "data:market:apis";
+
+ String MARKET_API_MASK_KEY = "data:market:api:masks";
+
+ String SYSTEM_DICT_KEY = "data:system:dicts";
+
+ String SYSTEM_CONFIG_KEY = "data:system:configs";
+
+ String METADATA_SOURCE_KEY = "data:metadata:sources";
+
+ String METADATA_TABLE_KEY = "data:metadata:tables";
+
+ String METADATA_COLUMN_KEY = "data:metadata:columns";
+
+ String METADATA_AUTHORIZE_KEY = "data:metadata:authorizes";
+
+ String STANDARD_DICT_KEY = "data:standard:dicts";
+
+ String WORKFLOW_BUSINESS_KEY = "data:workflow:business";
+
+ String VISUAL_SET_KEY = "data:visual:sets";
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/exception/BadRequestException.java b/studio/common/common-core/src/main/java/cn/datax/common/exception/BadRequestException.java
new file mode 100644
index 0000000..47ea097
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/exception/BadRequestException.java
@@ -0,0 +1,27 @@
+
+package cn.datax.common.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ * 统一异常处理
+ */
+@Getter
+public class BadRequestException extends RuntimeException{
+
+ private Integer status = BAD_REQUEST.value();
+
+ public BadRequestException(String msg){
+ super(msg);
+ }
+
+ public BadRequestException(HttpStatus status, String msg){
+ super(msg);
+ this.status = status.value();
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/exception/DataException.java b/studio/common/common-core/src/main/java/cn/datax/common/exception/DataException.java
new file mode 100644
index 0000000..cace168
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/exception/DataException.java
@@ -0,0 +1,27 @@
+package cn.datax.common.exception;
+
+public class DataException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public DataException() {
+ super();
+ }
+
+ public DataException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+ public DataException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DataException(String message) {
+ super(message);
+ }
+
+ public DataException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/exception/GlobalExceptionHandler.java b/studio/common/common-core/src/main/java/cn/datax/common/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..f2bfc48
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/exception/GlobalExceptionHandler.java
@@ -0,0 +1,64 @@
+package cn.datax.common.exception;
+
+import cn.datax.common.core.R;
+import cn.datax.common.utils.ThrowableUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.validation.BindException;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import java.util.stream.Collectors;
+
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ /**
+ * 处理自定义异常
+ */
+ @ExceptionHandler(DataException.class)
+ public R handleWithException(DataException e) {
+ log.error("自定义异常信息 ex={},StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e));
+ return R.error(e.getMessage());
+ }
+
+ /**
+ * 请求的 JSON 参数在请求体内的参数校验
+ */
+ @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
+ public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ String message = StringUtils.collectionToCommaDelimitedString(
+ e.getBindingResult().getFieldErrors()
+ .stream()
+ .map(FieldError::getDefaultMessage)
+ .collect(Collectors.toList()));
+ log.error("参数校验异常信息 ex={},StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e));
+ return R.error(message);
+ }
+
+ /**
+ * 请求的 URL 参数检验
+ */
+ @ExceptionHandler(ConstraintViolationException.class)
+ public R handleConstraintViolationException(ConstraintViolationException e) {
+ String message = StringUtils.collectionToCommaDelimitedString(
+ e.getConstraintViolations()
+ .stream()
+ .map(ConstraintViolation::getMessage)
+ .collect(Collectors.toList()));
+ log.error("参数校验异常信息 ex={},StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e));
+ return R.error(message);
+ }
+
+ @ExceptionHandler(Exception.class)
+ public R handleException(Exception e) {
+ log.error("全局异常信息ex={},StackTrace={}", e.getMessage(), ThrowableUtil.getStackTrace(e));
+ return R.error(e.getMessage());
+ }
+
+}
\ No newline at end of file
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/mapstruct/EntityMapper.java b/studio/common/common-core/src/main/java/cn/datax/common/mapstruct/EntityMapper.java
new file mode 100644
index 0000000..da06f8d
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/mapstruct/EntityMapper.java
@@ -0,0 +1,56 @@
+package cn.datax.common.mapstruct;
+
+import java.util.List;
+
+/**
+ *
+ * Mapper文件基类
+ * 更多的用法需自行实现
+ * @param 目标对象,一般为DTO对象
+ * @param 源对象,一般为需要转换的对象
+ * @param 目标对象,一般为VO对象
+ */
+public interface EntityMapper {
+
+ /**
+ * 将源对象转换为DTO对象
+ * @param e
+ * @return D
+ */
+ D toDTO(E e);
+
+ /**
+ * 将源对象集合转换为DTO对象集合
+ * @param es
+ * @return List
+ */
+ List toDTO(List es);
+
+ /**
+ * 将源对象转换为VO对象
+ * @param e
+ * @return D
+ */
+ V toVO(E e);
+
+ /**
+ * 将源对象集合转换为VO对象集合
+ * @param es
+ * @return List
+ */
+ List toVO(List es);
+
+ /**
+ * 将目标对象转换为源对象
+ * @param d
+ * @return E
+ */
+ E toEntity(D d);
+
+ /**
+ * 将目标对象集合转换为源对象集合
+ * @param ds
+ * @return List
+ */
+ List toEntity(List ds);
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/HttpUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/HttpUtil.java
new file mode 100644
index 0000000..747ef05
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/HttpUtil.java
@@ -0,0 +1,69 @@
+package cn.datax.common.utils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+public class HttpUtil {
+
+ public static final String METHOD_POST = "POST";
+ public static final String METHOD_GET = "GET";
+
+ public static String getBodyString(HttpServletRequest request) {
+ String method = request.getMethod();
+ String bodyString;
+ if (METHOD_GET.equals(method)) {
+ bodyString = doGet(request);
+ } else if (METHOD_POST.equals(method)) {
+ bodyString = doPost(request);
+ } else {
+ // 其他请求方式暂不处理
+ return null;
+ }
+ return bodyString;
+ }
+
+ private static String doPost(HttpServletRequest request) {
+ StringBuffer sb = new StringBuffer();
+ InputStream inputStream;
+ BufferedReader bufferedReader;
+ try {
+ //将数据保存到数组中,每次读取的时候,都读取一遍
+ inputStream = request.getInputStream();
+ //将字节数组当做输出的目的地
+ //字节流转换为字符流(处理流)
+ bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+ String line = "";
+ while ((line = bufferedReader.readLine()) != null) {
+ sb.append(line);
+ }
+ } catch (Exception e) {
+ log.error("数据读取异常", e);
+ }
+ return sb.toString();
+ }
+
+ private static String doGet(HttpServletRequest request) {
+ Map map = new HashMap<>();
+ Enumeration parameterNames = request.getParameterNames();
+ while (parameterNames.hasMoreElements()) {
+ String nextElement = parameterNames.nextElement();
+ String parameter = request.getParameter(nextElement);
+ map.put(nextElement, parameter);
+ }
+ try {
+ return new ObjectMapper().writeValueAsString(map);
+ } catch (JsonProcessingException e) {
+ }
+ return null;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/IPUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/IPUtil.java
new file mode 100644
index 0000000..d443291
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/IPUtil.java
@@ -0,0 +1,63 @@
+package cn.datax.common.utils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.*;
+import java.util.Enumeration;
+
+public class IPUtil {
+
+ public static String getLocalIP() throws SocketException {
+ String localIP = null;
+ Enumeration allNetInterfaces = NetworkInterface.getNetworkInterfaces();
+ InetAddress ip = null;
+ while (allNetInterfaces.hasMoreElements()) {
+ NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();
+ Enumeration addresses = netInterface.getInetAddresses();
+ while (addresses.hasMoreElements()) {
+ ip = (InetAddress) addresses.nextElement();
+ if (ip != null && ip instanceof Inet4Address) {
+ localIP = ip.getHostAddress();
+ if (!"127.0.0.1".equalsIgnoreCase(localIP)) {
+ return localIP;
+ }
+ }
+ }
+ }
+ return localIP;
+ }
+
+ /**
+ * 获取当前网络ip
+ * @param request
+ * @return
+ */
+ public static String getIpAddr(HttpServletRequest request) {
+ String ipAddress = request.getHeader("x-forwarded-for");
+ if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+ ipAddress = request.getHeader("Proxy-Client-IP");
+ }
+ if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+ ipAddress = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
+ ipAddress = request.getRemoteAddr();
+ if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {
+ //根据网卡取本机配置的IP
+ InetAddress inet = null;
+ try {
+ inet = InetAddress.getLocalHost();
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ }
+ ipAddress = inet.getHostAddress();
+ }
+ }
+ //对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
+ if (ipAddress != null && ipAddress.length() > 15) { //"***.***.***.***".length() = 15
+ if (ipAddress.indexOf(",") > 0) {
+ ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
+ }
+ }
+ return ipAddress;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/JwtUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/JwtUtil.java
new file mode 100644
index 0000000..63071f5
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/JwtUtil.java
@@ -0,0 +1,130 @@
+package cn.datax.common.utils;
+
+import cn.datax.service.system.api.dto.JwtUserDto;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jwts;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Date;
+
+@Slf4j
+public class JwtUtil {
+
+
+
+
+
+
+
+ /**
+ * 获取凭证信息
+ *
+ * @param token jwt token串
+ * @return Claims
+ */
+ public static Claims getClaimByToken(String token) {
+ try {
+ if (StringUtils.startsWithIgnoreCase(token, "Bearer ")) {
+ token = token.split(" ")[1];
+ }
+ return Jwts.parser()
+ .setSigningKey("ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=")
+ .parseClaimsJws(token)
+ .getBody();
+ }catch (Exception e){
+ HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ String authorization = request.getHeader("Authorization");
+ String url = request.getRequestURL().toString();
+ String uri = request.getRequestURI();
+ return null;
+ }
+ }
+
+
+ /**
+ * 获取过期时间
+ *
+ * @param token jwt token 串
+ * @return Date
+ */
+ public Date getExpiration(String token) {
+ return getClaimByToken(token).getExpiration();
+ }
+
+
+ /**
+ * 验证token是否失效
+ *
+ * @param token token
+ * @return true:过期 false:没过期
+ */
+ public boolean isExpired(String token) {
+ try {
+ final Date expiration = getExpiration(token);
+ return expiration.before(new Date());
+ } catch (Exception e) {
+ log.error("[JwtUtils --> isExpired]: {}", e.getMessage());
+ return true;
+ }
+ }
+
+
+// /**
+// * 检验是否为 jwt 格式的字符串
+// *
+// * 说明: jwt 字符串由三部分组成, 分别用 . 分隔开, 所以认为有两个 . 的字符串就是jwt形式的字符串
+// * @param token jwt token串
+// * @return boolean
+// */
+// public boolean isJwtStr(String token){
+// return StringUtils.countOccurrencesOf(token, ".") == 2;
+// }
+
+
+ /**
+ * 获取 jwt 中的账户名
+ *
+ * @param token jwt token 串
+ * @return String
+ */
+ public String getAccountName(String token){
+ String subject = getClaimByToken(token).getSubject();
+ JwtUserDto jwtContent = JSONObject.parseObject(subject, JwtUserDto.class);
+ jwtContent.getUsername();
+ return jwtContent.getUsername();
+ }
+
+
+ /**
+ * 获取 jwt 的账户对象
+ * @param token
+ * @return
+ */
+ public static String getTokenSubjectObject(String token){
+ Claims claimByToken = getClaimByToken(token);
+ String subject = claimByToken.getSubject();
+ String body = JSONObject.toJSONString(subject);
+ Object parse = JSON.parse(body);
+ String s = parse.toString();
+ return s;
+ }
+
+
+ /**
+ * 获取 jwt 账户信息的json字符串
+ * @param token
+ * @return
+ */
+ public String getTokenSubjectStr(String token){
+ String body = JSONObject.toJSONString(getClaimByToken(token).getSubject());
+ Object parse = JSON.parse(body);
+ return parse.toString();
+ }
+}
\ No newline at end of file
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/MD5Util.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/MD5Util.java
new file mode 100644
index 0000000..7e83fa6
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/MD5Util.java
@@ -0,0 +1,123 @@
+package cn.datax.common.utils;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.DESKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+import java.security.Key;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Base64;
+
+public class MD5Util {
+
+ /** 向量(同时拥有向量和密匙才能解密),此向量必须是8byte,多少都报错 */
+ private final byte[] DESIV = new byte[] { 0x22, 0x54, 0x36, 110, 0x40, (byte) 0xac, (byte) 0xad, (byte) 0xdf };
+ /** 自定义密钥,个数不能太短,太短报错,过长,它默认只取前N位(N的具体值,大家另行查找资料) */
+ private final String deSkey = "cloudcloud";
+ /** 加密算法的参数接口 */
+ private AlgorithmParameterSpec iv = null;
+ private Key key = null;
+ private String charset = "UTF-8";
+
+ private static volatile MD5Util instance;
+
+ /**
+ * 构造函数
+ * @throws Exception
+ */
+ private MD5Util() throws Exception {
+ // 设置密钥参数
+ DESKeySpec keySpec = new DESKeySpec(deSkey.getBytes(this.charset));
+ // 设置向量
+ iv = new IvParameterSpec(DESIV);
+ // 获得密钥工厂
+ SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
+ // 得到密钥对象
+ key = keyFactory.generateSecret(keySpec);
+ }
+
+ public static MD5Util getInstance() throws Exception {
+ if(instance == null) {
+ synchronized (MD5Util.class) {
+ if(instance == null) {
+ instance = new MD5Util();
+ }
+ }
+ }
+ return instance;
+ }
+
+ public static void main(String[] args) {
+ try {
+ String value = "1246656415670484994";
+ MD5Util mt = new MD5Util();
+ System.out.println("加密前的字符:" + value);
+ System.out.println("加密后的字符:" + mt.encode(value));
+ System.out.println("解密后的字符:" + mt.decode(mt.encode(value)));
+ System.out.println("字符串的MD5值:"+ getMD5Value(value));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 加密
+ * @param data
+ * @return
+ * @throws Exception
+ */
+ public String encode(String data) throws Exception {
+ // 得到加密对象Cipher
+ Cipher enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
+ // 设置工作模式为加密模式,给出密钥和向量
+ enCipher.init(Cipher.ENCRYPT_MODE, key, iv);
+ byte[] pasByte = enCipher.doFinal(data.getBytes(this.charset));
+ return Base64.getEncoder().encodeToString(pasByte);
+ }
+
+ /**
+ * 解密
+ * @param data
+ * @return
+ * @throws Exception
+ */
+ public String decode(String data) throws Exception {
+ Cipher deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
+ deCipher.init(Cipher.DECRYPT_MODE, key, iv);
+ //此处注意doFinal()的参数的位数必须是8的倍数,否则会报错(通过encode加密的字符串读出来都是8的倍数位,但写入文件再读出来,就可能因为读取的方式的问题,导致最后此处的doFinal()的参数的位数不是8的倍数)
+ //此处必须用base64Decoder,若用data。getBytes()则获取的字符串的byte数组的个数极可能不是8的倍数,而且不与上面的BASE64Encoder对应(即使解密不报错也不会得到正确结果)
+ byte[] pasByte = deCipher.doFinal(Base64.getDecoder().decode(data));
+ return new String(pasByte, this.charset);
+ }
+
+ /**
+ * 获取MD5的值,可用于对比校验
+ * @param sourceStr
+ * @return
+ */
+ private static String getMD5Value(String sourceStr) {
+ String result = "";
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ md.update(sourceStr.getBytes());
+ byte b[] = md.digest();
+ int i;
+ StringBuffer buf = new StringBuffer("");
+ for (int offset = 0; offset < b.length; offset++) {
+ i = b[offset];
+ if (i < 0) {
+ i += 256;
+ }
+ if (i < 16) {
+ buf.append("0");
+ }
+ buf.append(Integer.toHexString(i));
+ }
+ result = buf.toString();
+ } catch (NoSuchAlgorithmException e) {
+ }
+ return result;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/MsgFormatUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/MsgFormatUtil.java
new file mode 100644
index 0000000..14c2bf8
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/MsgFormatUtil.java
@@ -0,0 +1,55 @@
+package cn.datax.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * 消息模板格式化
+ */
+public class MsgFormatUtil {
+
+ private static String REGEX = "(\\{([a-zA-Z]+)\\})";
+
+ public static String TEMPALTE_NICKNAME = "nickname";
+ public static String TEMPALTE_DATETIME = "datetime";
+ public static String TEMPALTE_BUSINESS_NAME = "businessName";
+ public static String TEMPALTE_BUSINESS_KEY = "businessKey";
+
+ /**
+ * 根据模板及参数获得内容
+ * @param tempalte
+ * @param parameters
+ * @return
+ */
+ public static String getContent(String tempalte, Map parameters) {
+ if (StrUtil.isBlank(tempalte)) {
+ tempalte = "业务名称:{businessName},发起人:{nickname},业务编号:{businessKey}";
+ }
+ Pattern p = Pattern.compile(REGEX);
+ Matcher m = p.matcher(tempalte);
+ StringBuffer stringBuffer = new StringBuffer();
+ while (m.find()){
+ String key = m.group(2);
+ String value = null;
+ if (parameters.containsKey(key)){
+ value = parameters.get(key);
+ }
+ value = (value == null) ? "" : value;
+ m.appendReplacement(stringBuffer, value);
+ }
+ m.appendTail(stringBuffer);
+ return stringBuffer.toString();
+ }
+
+ public static void main(String[] args) {
+ String tempalte = "{name}你好,今年{age}岁";
+ Map parameters = new HashMap<>();
+ parameters.put("name", "chris");
+ parameters.put("age", "22");
+ System.out.println(getContent(tempalte, parameters));
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/PageUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/PageUtil.java
new file mode 100644
index 0000000..ecbf17f
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/PageUtil.java
@@ -0,0 +1,53 @@
+package cn.datax.common.utils;
+
+import java.io.Serializable;
+
+public class PageUtil implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private static Integer DEFAULT_MAX_COUNT = 5000;
+
+ // 当前页码
+ private Integer pageNum = 1;
+ // 分页条数
+ private Integer pageSize = 20;
+
+ public Integer getPageNum() {
+ return pageNum;
+ }
+
+ public void setPageNum(Integer pageNum) {
+ this.pageNum = pageNum;
+ }
+
+ public Integer getPageSize() {
+ return pageSize;
+ }
+
+ public void setPageSize(Integer pageSize) {
+ this.pageSize = pageSize;
+ if (this.pageSize > 0) {
+ this.pageSize = this.pageSize > DEFAULT_MAX_COUNT ? DEFAULT_MAX_COUNT : this.pageSize;
+ } else {
+ this.pageSize = 20;
+ }
+ }
+
+ public PageUtil(Integer pageNum, Integer pageSize) {
+ this.pageNum = pageNum;
+ this.pageSize = pageSize;
+ if (this.pageSize > 0) {
+ this.pageSize = this.pageSize > DEFAULT_MAX_COUNT ? DEFAULT_MAX_COUNT : this.pageSize;
+ } else {
+ this.pageSize = 20;
+ }
+ }
+
+ public Integer getOffset() {
+ pageSize = pageSize == null ? 20 : pageSize;
+ pageNum = pageNum == null ? 1 : pageNum;
+ int offset = pageNum > 0 ? (pageNum - 1) * pageSize : 0;
+ return offset;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/RequestHolder.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/RequestHolder.java
new file mode 100644
index 0000000..416442a
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/RequestHolder.java
@@ -0,0 +1,22 @@
+package cn.datax.common.utils;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * 获取 HttpServletRequest
+ *
+ */
+public class RequestHolder {
+
+ public static HttpServletRequest getHttpServletRequest() {
+ return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ }
+
+ public static HttpServletResponse getHttpServletResponse() {
+ return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/ResponseUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/ResponseUtil.java
new file mode 100644
index 0000000..bd4849c
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/ResponseUtil.java
@@ -0,0 +1,27 @@
+package cn.datax.common.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class ResponseUtil {
+
+ /**
+ * 设置响应
+ *
+ * @param response HttpServletResponse
+ * @param contentType content-type
+ * @param status http状态码
+ * @param value 响应内容
+ * @throws IOException IOException
+ */
+ public static void makeResponse(HttpServletResponse response, String contentType,
+ int status, Object value) throws IOException {
+ response.setContentType(contentType);
+ response.setStatus(status);
+ ObjectMapper objectMapper = new ObjectMapper();
+ response.getOutputStream().write(objectMapper.writeValueAsBytes(value));
+ }
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtil.java
new file mode 100644
index 0000000..57b757f
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtil.java
@@ -0,0 +1,129 @@
+package cn.datax.common.utils;
+
+
+import com.platform.exception.BadRequestException;
+
+
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.datax.service.system.api.feign.UserServiceFeign;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class SecurityUtil {
+
+ @Autowired
+ private UserServiceFeign userServiceFeign;
+
+ @Autowired
+ private JwtUtil jwtUtil;
+
+ /**
+ * 获取用户
+ *
+ * @return user
+ */
+ public static JwtUserDto getDataUser() {
+ UserServiceFeign userServiceFeign = SpringContextHolder.getBean(UserServiceFeign.class);
+ return userServiceFeign.loginByUsername(getCurrentUsername());
+ }
+
+ public static String getCurrentUsername() {
+ HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ String authorization = request.getHeader("Authorization");
+ if (null == authorization) {
+ authorization = RequestContextHolder.getRequestAttributes().getAttribute("token", 1).toString();
+ }
+ String tokenSubjectObject = JwtUtil.getTokenSubjectObject(authorization);
+ if (tokenSubjectObject == null) {
+ throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
+ }
+ return tokenSubjectObject;
+
+ }
+
+
+ /**
+ * 获取用户ID
+ *
+ * @return id
+ */
+ public static String getUserId() {
+ JwtUserDto user = getDataUser();
+ if (user != null) {
+ return user.getUser().getId() + "";
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户部门
+ *
+ * @return id
+ */
+ public static String getUserDeptId() {
+ JwtUserDto user = getDataUser();
+ if (user != null) {
+ return user.getUser().getDeptId() + "";
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户名称
+ *
+ * @return username
+ */
+ public static String getUserName() {
+ JwtUserDto user = getDataUser();
+ if (user != null) {
+ return user.getUsername();
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户昵称
+ *
+ * @return nickname
+ */
+ public static String getNickname() {
+ JwtUserDto user = getDataUser();
+ if (user != null) {
+ return user.getUser().getNickName();
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户角色
+ *
+ * @return username
+ */
+ public static List getUserRoleIds() {
+ JwtUserDto user = getDataUser();
+ if (user != null) {
+ List roles = new ArrayList<>(user.getRoles());
+ return roles;
+ }
+ return null;
+ }
+
+ /**
+ * 获取用户
+ *
+ * @return user
+ */
+ public static boolean isAdmin() {
+ return true;
+ }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtils.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtils.java
new file mode 100644
index 0000000..3ee35e3
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/SecurityUtils.java
@@ -0,0 +1,82 @@
+
+package cn.datax.common.utils;
+
+import cn.datax.common.exception.BadRequestException;
+import cn.hutool.json.JSONArray;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+import java.util.List;
+
+/**
+ * 获取当前登录的用户
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Slf4j
+public class SecurityUtils {
+
+ /**
+ * 获取当前登录的用户
+ * @return UserDetails
+ */
+ public static UserDetails getCurrentUser() {
+ UserDetailsService userDetailsService = SpringContextHolder.getBean(UserDetailsService.class);
+ return userDetailsService.loadUserByUsername(getCurrentUsername());
+ }
+
+ /**
+ * 获取系统用户名称
+ *
+ * @return 系统用户名称
+ */
+ public static String getCurrentUsername() {
+ final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication == null) {
+ throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
+ }
+ if (authentication.getPrincipal() instanceof UserDetails) {
+ UserDetails userDetails = (UserDetails) authentication.getPrincipal();
+ return userDetails.getUsername();
+ }
+ throw new BadRequestException(HttpStatus.UNAUTHORIZED, "找不到当前登录的信息");
+ }
+
+ /**
+ * 获取系统用户ID
+ * @return 系统用户ID
+ */
+ public static Long getCurrentUserId() {
+ UserDetails userDetails = getCurrentUser();
+ return new JSONObject(new JSONObject(userDetails).get("user")).get("id", Long.class);
+ }
+
+ /**
+ * 获取当前用户的数据权限
+ * @return /
+ */
+ public static List getCurrentUserDataScope(){
+ UserDetails userDetails = getCurrentUser();
+ JSONArray array = JSONUtil.parseArray(new JSONObject(userDetails).get("dataScopes"));
+ return JSONUtil.toList(array,Long.class);
+ }
+
+// /**
+// * 获取数据权限级别
+// * @return 级别
+// */
+// public static String getDataScopeType() {
+// List dataScopes = getCurrentUserDataScope();
+// if(dataScopes.size() != 0){
+// return "";
+// }
+// return DataScopeEnum.ALL.getValue();
+// }
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/SpringContextHolder.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/SpringContextHolder.java
new file mode 100644
index 0000000..945d438
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/SpringContextHolder.java
@@ -0,0 +1,67 @@
+package cn.datax.common.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+@Component
+@Lazy(false)
+public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
+
+ private static ApplicationContext applicationContext = null;
+
+ private static Logger logger = LoggerFactory.getLogger(SpringContextHolder.class);
+
+ /**
+ * 取得存储在静态变量中的ApplicationContext.
+ */
+ public static ApplicationContext getApplicationContext() {
+ return applicationContext;
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ @SuppressWarnings("unchecked")
+ public static T getBean(String name) {
+ return (T) applicationContext.getBean(name);
+ }
+
+ /**
+ * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
+ */
+ public static T getBean(Class requiredType) {
+ return applicationContext.getBean(requiredType);
+ }
+
+ /**
+ * 清除SpringContextHolder中的ApplicationContext为Null.
+ */
+ public static void clearHolder() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
+ }
+ applicationContext = null;
+ }
+
+ /**
+ * 实现ApplicationContextAware接口, 注入Context到静态变量中.
+ */
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ SpringContextHolder.applicationContext = applicationContext;
+ }
+
+ /**
+ * 实现DisposableBean接口, 在Context关闭时清理静态变量.
+ */
+ @Override
+ public void destroy() throws Exception {
+ SpringContextHolder.clearHolder();
+ }
+
+}
diff --git a/studio/common/common-core/src/main/java/cn/datax/common/utils/ThrowableUtil.java b/studio/common/common-core/src/main/java/cn/datax/common/utils/ThrowableUtil.java
new file mode 100644
index 0000000..d40ec9b
--- /dev/null
+++ b/studio/common/common-core/src/main/java/cn/datax/common/utils/ThrowableUtil.java
@@ -0,0 +1,26 @@
+package cn.datax.common.utils;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * 异常工具
+ */
+public class ThrowableUtil {
+
+ /**
+ * 获取堆栈信息
+ * @param throwable
+ * @return
+ */
+ public static String getStackTrace(Throwable throwable){
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ try {
+ throwable.printStackTrace(pw);
+ return sw.toString();
+ } finally {
+ pw.close();
+ }
+ }
+}
diff --git a/studio/common/common-core/src/main/resources/META-INF/spring.factories b/studio/common/common-core/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..2baebfb
--- /dev/null
+++ b/studio/common/common-core/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.config.JacksonConfig,\
+ cn.datax.common.config.RestTemplateConfig,\
+ cn.datax.common.exception.GlobalExceptionHandler,\
+ cn.datax.common.utils.SpringContextHolder
diff --git a/studio/common/common-database/pom.xml b/studio/common/common-database/pom.xml
new file mode 100644
index 0000000..0511c46
--- /dev/null
+++ b/studio/common/common-database/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-database
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.version}
+
+
+ org.mariadb.jdbc
+ mariadb-java-client
+ ${mariadb.version}
+
+
+ com.oracle.database.jdbc
+ ojdbc8
+ ${oracle.version}
+
+
+ org.postgresql
+ postgresql
+ ${postgresql.version}
+
+
+ com.microsoft.sqlserver
+ mssql-jdbc
+ ${sqlserver.version}
+
+
+ com.dameng
+ DmJdbcDriver18
+ ${dameng.version}
+
+
+
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/DataSourceFactory.java b/studio/common/common-database/src/main/java/cn/datax/common/database/DataSourceFactory.java
new file mode 100644
index 0000000..71f0ae7
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/DataSourceFactory.java
@@ -0,0 +1,14 @@
+package cn.datax.common.database;
+
+import cn.datax.common.database.constants.DbQueryProperty;
+
+public interface DataSourceFactory {
+
+ /**
+ * 创建数据源实例
+ *
+ * @param property
+ * @return
+ */
+ DbQuery createDbQuery(DbQueryProperty property);
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/DbDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/DbDialect.java
new file mode 100644
index 0000000..fc6003c
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/DbDialect.java
@@ -0,0 +1,62 @@
+package cn.datax.common.database;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import org.springframework.jdbc.core.RowCallbackHandler;
+import org.springframework.jdbc.core.RowMapper;
+
+/**
+ * 表数据查询接口
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public interface DbDialect {
+
+ RowMapper tableMapper();
+
+ RowMapper columnMapper();
+
+ /**
+ * 获取指定表的所有列
+ *
+ * @param dbName
+ * @param tableName
+ * @return
+ */
+ String columns(String dbName, String tableName);
+
+ /**
+ * 获取数据库下的 所有表
+ *
+ * @param dbName
+ * @return
+ */
+ String tables(String dbName);
+
+ /**
+ * 构建 分页 sql
+ *
+ * @param sql
+ * @param offset
+ * @param count
+ * @return
+ */
+ String buildPaginationSql(String sql, long offset, long count);
+
+ /**
+ * 包装 count sql
+ *
+ * @param sql
+ * @return
+ */
+ String count(String sql);
+
+ /**
+ * oracl 读取long 类型会流关闭,是oracle的bug,需要特殊处理
+ * @return
+ */
+ default RowMapper columnLongMapper() {
+ return null;
+ };
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/DbQuery.java b/studio/common/common-database/src/main/java/cn/datax/common/database/DbQuery.java
new file mode 100644
index 0000000..0e97c58
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/DbQuery.java
@@ -0,0 +1,121 @@
+package cn.datax.common.database;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.core.PageResult;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 表数据查询接口
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public interface DbQuery {
+
+ /**
+ * 获取数据库连接
+ */
+ Connection getConnection();
+
+ /**
+ * 检测连通性
+ */
+ boolean valid();
+
+ /**
+ * 关闭数据源
+ */
+ void close();
+
+ /**
+ * 获取指定表 具有的所有字段列表
+ * @param dbName
+ * @param tableName
+ * @return
+ */
+ List getTableColumns(String dbName, String tableName);
+
+ /**
+ * 获取指定数据库下 所有的表信息
+ *
+ * @param dbName
+ * @return
+ */
+ List getTables(String dbName);
+
+ /**
+ * 获取总数
+ *
+ * @param sql
+ * @return
+ */
+ int count(String sql);
+
+ /**
+ * 获取总数带查询参数
+ *
+ * @param sql
+ * @return
+ */
+ int count(String sql, Object[] args);
+
+ /**
+ * 获取总数带查询参数 NamedParameterJdbcTemplate
+ *
+ * @param sql
+ * @return
+ */
+ int count(String sql, Map params);
+
+ /**
+ * 查询结果列表
+ *
+ * @param sql
+ * @return
+ */
+ List> queryList(String sql);
+
+ /**
+ * 查询结果列表带查询参数
+ *
+ * @param sql
+ * @param args
+ * @return
+ */
+ List> queryList(String sql, Object[] args);
+
+ /**
+ * 查询结果分页
+ *
+ * @param sql
+ * @param offset
+ * @param size
+ * @return
+ */
+ PageResult> queryByPage(String sql, long offset, long size);
+
+ /**
+ * 查询结果分页带查询参数
+ * @param sql
+ * @param args
+ * @param offset
+ * @param size
+ * @return
+ */
+ PageResult> queryByPage(String sql, Object[] args, long offset, long size);
+
+ /**
+ * 查询结果分页带查询参数 NamedParameterJdbcTemplate
+ * @param sql
+ * @param params
+ * @param offset
+ * @param size
+ * @return
+ */
+ PageResult> queryByPage(String sql, Map params, long offset, long size);
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/DialectFactory.java b/studio/common/common-database/src/main/java/cn/datax/common/database/DialectFactory.java
new file mode 100644
index 0000000..c390571
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/DialectFactory.java
@@ -0,0 +1,19 @@
+package cn.datax.common.database;
+
+import cn.datax.common.database.constants.DbType;
+import cn.datax.common.database.dialect.DialectRegistry;
+
+/**
+ * 方言工厂类
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class DialectFactory {
+
+ private static final DialectRegistry DIALECT_REGISTRY = new DialectRegistry();
+
+ public static DbDialect getDialect(DbType dbType) {
+ return DIALECT_REGISTRY.getDialect(dbType);
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/cache/DefaultSqlCache.java b/studio/common/common-database/src/main/java/cn/datax/common/database/cache/DefaultSqlCache.java
new file mode 100644
index 0000000..4116e52
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/cache/DefaultSqlCache.java
@@ -0,0 +1,123 @@
+package cn.datax.common.database.cache;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class DefaultSqlCache extends LinkedHashMap> implements SqlCache {
+
+ private int capacity;
+
+ private long expire;
+
+ private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+
+ public DefaultSqlCache(int capacity, long expire) {
+ super((int) Math.ceil(capacity / 0.75) + 1, 0.75f, true);
+ // 容量
+ this.capacity = capacity;
+ // 固定过期时间
+ this.expire = expire;
+ }
+
+ @Override
+ public void put(String key, Object value, long ttl) {
+ long expireTime = Long.MAX_VALUE;
+ if (ttl >= 0) {
+ expireTime = System.currentTimeMillis() + (ttl == 0 ? this.expire : ttl);
+ }
+ lock.writeLock().lock();
+ try {
+ // 封装成过期时间节点
+ put(key, new ExpireNode<>(expireTime, value));
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+ @Override
+ public Object get(String key) {
+ lock.readLock().lock();
+ ExpireNode expireNode;
+ try {
+ expireNode = super.get(key);
+ } finally {
+ lock.readLock().unlock();
+ }
+ if (expireNode == null) {
+ return null;
+ }
+ // 惰性删除过期的
+ if (this.expire > -1L && expireNode.expire < System.currentTimeMillis()) {
+ try {
+ lock.writeLock().lock();
+ super.remove(key);
+ } finally {
+ lock.writeLock().unlock();
+ }
+ return null;
+ }
+ return expireNode.value;
+ }
+
+ @Override
+ public void delete(String key) {
+ try {
+ lock.writeLock().lock();
+ Iterator>> iterator = super.entrySet().iterator();
+ // 清除key的缓存
+ while (iterator.hasNext()) {
+ Map.Entry> entry = iterator.next();
+ if (entry.getKey().equals(key)) {
+ iterator.remove();
+ }
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+
+ @Override
+ protected boolean removeEldestEntry(Map.Entry> eldest) {
+ if (this.expire > -1L && size() > capacity) {
+ clean();
+ }
+ // lru淘汰
+ return size() > this.capacity;
+ }
+
+ /**
+ * 清理已过期的数据
+ */
+ private void clean() {
+ try {
+ lock.writeLock().lock();
+ Iterator>> iterator = super.entrySet().iterator();
+ long now = System.currentTimeMillis();
+ while (iterator.hasNext()) {
+ Map.Entry> next = iterator.next();
+ // 判断是否过期
+ if (next.getValue().expire < now) {
+ iterator.remove();
+ }
+ }
+ } finally {
+ lock.writeLock().unlock();
+ }
+ }
+
+
+ /**
+ * 过期时间节点
+ */
+ static class ExpireNode {
+ long expire;
+ Object value;
+ public ExpireNode(long expire, Object value) {
+ this.expire = expire;
+ this.value = value;
+ }
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/cache/SqlCache.java b/studio/common/common-database/src/main/java/cn/datax/common/database/cache/SqlCache.java
new file mode 100644
index 0000000..80bc0ad
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/cache/SqlCache.java
@@ -0,0 +1,38 @@
+package cn.datax.common.database.cache;
+
+import cn.datax.common.database.utils.MD5Util;
+
+import java.util.Arrays;
+
+/**
+ * SQL缓存接口
+ */
+public interface SqlCache {
+
+ /**
+ * 计算key
+ */
+ default String buildSqlCacheKey(String sql, Object[] args) {
+ return MD5Util.encrypt(sql + ":" + Arrays.toString(args));
+ }
+
+ /**
+ * 存入缓存
+ * @param key key
+ * @param value 值
+ */
+ void put(String key, Object value, long ttl);
+
+ /**
+ * 获取缓存
+ * @param key key
+ * @return
+ */
+ T get(String key);
+
+ /**
+ * 删除缓存
+ * @param key key
+ */
+ void delete(String key);
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbQueryProperty.java b/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbQueryProperty.java
new file mode 100644
index 0000000..c73acba
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbQueryProperty.java
@@ -0,0 +1,37 @@
+package cn.datax.common.database.constants;
+
+import cn.datax.common.database.exception.DataQueryException;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.springframework.util.StringUtils;
+
+import java.io.Serializable;
+
+@Data
+@AllArgsConstructor
+public class DbQueryProperty implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private String dbType;
+ private String host;
+ private String username;
+ private String password;
+ private Integer port;
+ private String dbName;
+ private String sid;
+
+ /**
+ * 参数合法性校验
+ */
+ public void viald() {
+ if (StringUtils.isEmpty(dbType) || StringUtils.isEmpty(host) ||
+ StringUtils.isEmpty(username) || StringUtils.isEmpty(password) ||
+ StringUtils.isEmpty(port)) {
+ throw new DataQueryException("参数不完整");
+ }
+ if (DbType.OTHER.getDb().equals(dbType)) {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbType.java b/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbType.java
new file mode 100644
index 0000000..0936d25
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/constants/DbType.java
@@ -0,0 +1,94 @@
+package cn.datax.common.database.constants;
+
+/**
+ * 数据库类型
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public enum DbType {
+
+ /**
+ * MYSQL
+ */
+ MYSQL("1", "MySql数据库", "jdbc:mysql://${host}:${port}/${dbName}?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8"),
+ /**
+ * MARIADB
+ */
+ MARIADB("2", "MariaDB数据库", "jdbc:mariadb://${host}:${port}/${dbName}"),
+ /**
+ * ORACLE
+ */
+ ORACLE("3", "Oracle11g及以下数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"),
+ /**
+ * oracle12c new pagination
+ */
+ ORACLE_12C("4", "Oracle12c+数据库", "jdbc:oracle:thin:@${host}:${port}:${sid}"),
+ /**
+ * POSTGRESQL
+ */
+ POSTGRE_SQL("5", "PostgreSql数据库", "jdbc:postgresql://${host}:${port}/${dbName}"),
+ /**
+ * SQLSERVER2005
+ */
+ SQL_SERVER2008("6", "SQLServer2008及以下数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"),
+ /**
+ * SQLSERVER
+ */
+ SQL_SERVER("7", "SQLServer2012+数据库", "jdbc:sqlserver://${host}:${port};DatabaseName=${dbName}"),
+ /**
+ * DMDb
+ */
+ DMDB("9", "DM数据库", "jdbc:dm://${host}:${port}/${dbName}"),
+ /**
+ * UNKONWN DB
+ */
+ OTHER("8", "其他数据库", "");
+
+ /**
+ * 数据库名称
+ */
+ private final String db;
+
+ /**
+ * 描述
+ */
+ private final String desc;
+
+ /**
+ * url
+ */
+ private final String url;
+
+ public String getDb() {
+ return this.db;
+ }
+
+ public String getDesc() {
+ return this.desc;
+ }
+
+ public String getUrl() {
+ return this.url;
+ }
+
+ DbType(String db, String desc, String url) {
+ this.db = db;
+ this.desc = desc;
+ this.url = url;
+ }
+
+ /**
+ * 获取数据库类型
+ *
+ * @param dbType 数据库类型字符串
+ */
+ public static DbType getDbType(String dbType) {
+ for (DbType type : DbType.values()) {
+ if (type.db.equals(dbType)) {
+ return type;
+ }
+ }
+ return OTHER;
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbColumn.java b/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbColumn.java
new file mode 100644
index 0000000..2ceca2b
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbColumn.java
@@ -0,0 +1,57 @@
+package cn.datax.common.database.core;
+
+import lombok.Data;
+
+@Data
+public class DbColumn {
+
+ /**
+ * 列名
+ */
+ private String colName;
+
+ /**
+ * 数据类型
+ */
+ private String dataType;
+
+ /**
+ * 数据长度
+ */
+ private String dataLength;
+
+ /**
+ * 数据精度
+ */
+ private String dataPrecision;
+
+ /**
+ * 数据小数位
+ */
+ private String dataScale;
+
+ /**
+ * 是否主键
+ */
+ private Boolean colKey;
+
+ /**
+ * 是否允许为空
+ */
+ private Boolean nullable;
+
+ /**
+ * 列的序号
+ */
+ private Integer colPosition;
+
+ /**
+ * 列默认值
+ */
+ private String dataDefault;
+
+ /**
+ * 列注释
+ */
+ private String colComment;
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbTable.java b/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbTable.java
new file mode 100644
index 0000000..e01e87e
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/core/DbTable.java
@@ -0,0 +1,17 @@
+package cn.datax.common.database.core;
+
+import lombok.Data;
+
+@Data
+public class DbTable {
+
+ /**
+ * 表名
+ */
+ private String tableName;
+
+ /**
+ * 表注释
+ */
+ private String tableComment;
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/core/PageResult.java b/studio/common/common-database/src/main/java/cn/datax/common/database/core/PageResult.java
new file mode 100644
index 0000000..3aeb7f5
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/core/PageResult.java
@@ -0,0 +1,24 @@
+package cn.datax.common.database.core;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class PageResult implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ private Integer pageNum;
+ private Integer pageSize;
+ private Integer total;
+ private List data;
+
+ public PageResult(Integer total, List data) {
+ this.total = total;
+ this.data = data;
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/AbstractDataSourceFactory.java b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/AbstractDataSourceFactory.java
new file mode 100644
index 0000000..d3dd02b
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/AbstractDataSourceFactory.java
@@ -0,0 +1,63 @@
+package cn.datax.common.database.datasource;
+
+import cn.datax.common.database.*;
+import cn.datax.common.database.constants.DbQueryProperty;
+import cn.datax.common.database.constants.DbType;
+import cn.datax.common.database.exception.DataQueryException;
+import cn.datax.common.database.query.AbstractDbQueryFactory;
+import cn.datax.common.database.query.CacheDbQueryFactoryBean;
+import com.zaxxer.hikari.HikariDataSource;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.util.StringUtils;
+
+import javax.sql.DataSource;
+
+public abstract class AbstractDataSourceFactory implements DataSourceFactory {
+
+ @Override
+ public DbQuery createDbQuery(DbQueryProperty property) {
+ property.viald();
+ DbType dbType = DbType.getDbType(property.getDbType());
+ DataSource dataSource = createDataSource(property);
+ DbQuery dbQuery = createDbQuery(dataSource, dbType);
+ return dbQuery;
+ }
+
+ public DbQuery createDbQuery(DataSource dataSource, DbType dbType) {
+ DbDialect dbDialect = DialectFactory.getDialect(dbType);
+ if(dbDialect == null){
+ throw new DataQueryException("该数据库类型正在开发中");
+ }
+ AbstractDbQueryFactory dbQuery = new CacheDbQueryFactoryBean();
+ dbQuery.setDataSource(dataSource);
+ dbQuery.setJdbcTemplate(new JdbcTemplate(dataSource));
+ dbQuery.setDbDialect(dbDialect);
+ return dbQuery;
+ }
+
+ public DataSource createDataSource(DbQueryProperty property) {
+ HikariDataSource dataSource = new HikariDataSource();
+// if (property.getDbType().equals("9")) {
+// dataSource.setDriverClassName("dm.jdbc.driver.DmDriver");
+// }
+ dataSource.setJdbcUrl(trainToJdbcUrl(property));
+ dataSource.setUsername(property.getUsername());
+ dataSource.setPassword(property.getPassword());
+ return dataSource;
+ }
+
+ protected String trainToJdbcUrl(DbQueryProperty property) {
+ String url = DbType.getDbType(property.getDbType()).getUrl();
+ if (StringUtils.isEmpty(url)) {
+ throw new DataQueryException("无效数据库类型!");
+ }
+ url = url.replace("${host}", property.getHost());
+ url = url.replace("${port}", String.valueOf(property.getPort()));
+ if (DbType.ORACLE.getDb().equals(property.getDbType()) || DbType.ORACLE_12C.getDb().equals(property.getDbType())) {
+ url = url.replace("${sid}", property.getSid());
+ } else {
+ url = url.replace("${dbName}", property.getDbName());
+ }
+ return url;
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/CacheDataSourceFactoryBean.java b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/CacheDataSourceFactoryBean.java
new file mode 100644
index 0000000..772557e
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/CacheDataSourceFactoryBean.java
@@ -0,0 +1,64 @@
+package cn.datax.common.database.datasource;
+
+import cn.datax.common.database.constants.DbQueryProperty;
+
+import javax.sql.DataSource;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class CacheDataSourceFactoryBean extends AbstractDataSourceFactory {
+
+ /**
+ * 数据源缓存
+ */
+ private static Map dataSourceMap = new ConcurrentHashMap<>();
+
+ @Override
+ public DataSource createDataSource(DbQueryProperty property) {
+ String key = property.getDbType() + ":" + property.getHost()
+ + ":" + property.getPort() + ":" + property.getUsername()
+ + ":" + property.getPassword() + ":" + property.getDbName()
+ + ":" + property.getSid();
+ String s = compress(key);
+ DataSource dataSource = dataSourceMap.get(s);
+ if (null == dataSource) {
+ synchronized (CacheDataSourceFactoryBean.class) {
+ dataSource = super.createDataSource(property);
+ dataSourceMap.put(s, dataSource);
+ }
+ }
+ return dataSource;
+ }
+
+ // 压缩
+ public static String compress(String str) {
+ if (str == null || str.length() == 0) {
+ return str;
+ }
+ MessageDigest md = null;
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ md.update(str.getBytes());
+ byte b[] = md.digest();
+ int i;
+ StringBuffer buf = new StringBuffer();
+ for (int offset = 0; offset < b.length; offset++) {
+ i = b[offset];
+ if (i < 0)
+ i += 256;
+ if (i < 16)
+ buf.append("0");
+ buf.append(Integer.toHexString(i));
+ }
+// System.out.println("MD5(" + str + ",32小写) = " + buf.toString());
+// System.out.println("MD5(" + str + ",32大写) = " + buf.toString().toUpperCase());
+// System.out.println("MD5(" + str + ",16小写) = " + buf.toString().substring(8, 24));
+// System.out.println("MD5(" + str + ",16大写) = " + buf.toString().substring(8, 24).toUpperCase());
+ return buf.toString().substring(8, 24).toUpperCase();
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/DefaultDataSourceFactoryBean.java b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/DefaultDataSourceFactoryBean.java
new file mode 100644
index 0000000..39848fb
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/datasource/DefaultDataSourceFactoryBean.java
@@ -0,0 +1,4 @@
+package cn.datax.common.database.datasource;
+
+public class DefaultDataSourceFactoryBean extends AbstractDataSourceFactory {
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/AbstractDbDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/AbstractDbDialect.java
new file mode 100644
index 0000000..a2b76a6
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/AbstractDbDialect.java
@@ -0,0 +1,37 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.DbDialect;
+
+/**
+ * 方言抽象类
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public abstract class AbstractDbDialect implements DbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ return "select column_name AS COLNAME, ordinal_position AS COLPOSITION, column_default AS DATADEFAULT, is_nullable AS NULLABLE, data_type AS DATATYPE, " +
+ "character_maximum_length AS DATALENGTH, numeric_precision AS DATAPRECISION, numeric_scale AS DATASCALE, column_key AS COLKEY, column_comment AS COLCOMMENT " +
+ "from information_schema.columns where table_schema = '" + dbName + "' and table_name = '" + tableName + "' order by ordinal_position ";
+ }
+
+ @Override
+ public String tables(String dbName) {
+ return "SELECT table_name AS TABLENAME, table_comment AS TABLECOMMENT FROM information_schema.tables where table_schema = '" + dbName + "' ";
+ }
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ // 获取 分页实际条数
+ StringBuilder sqlBuilder = new StringBuilder(originalSql);
+ sqlBuilder.append(" LIMIT ").append(offset).append(" , ").append(count);
+ return sqlBuilder.toString();
+ }
+
+ @Override
+ public String count(String sql) {
+ return "SELECT COUNT(*) FROM ( " + sql + " ) TEMP";
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DialectRegistry.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DialectRegistry.java
new file mode 100644
index 0000000..232a452
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DialectRegistry.java
@@ -0,0 +1,28 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.DbDialect;
+import cn.datax.common.database.constants.DbType;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+public class DialectRegistry {
+
+ private final Map dialect_enum_map = new EnumMap<>(DbType.class);
+
+ public DialectRegistry() {
+ dialect_enum_map.put(DbType.MARIADB, new MariaDBDialect());
+ dialect_enum_map.put(DbType.MYSQL, new MySqlDialect());
+ dialect_enum_map.put(DbType.ORACLE_12C, new Oracle12cDialect());
+ dialect_enum_map.put(DbType.ORACLE, new OracleDialect());
+ dialect_enum_map.put(DbType.POSTGRE_SQL, new PostgreDialect());
+ dialect_enum_map.put(DbType.SQL_SERVER2008, new SQLServer2008Dialect());
+ dialect_enum_map.put(DbType.SQL_SERVER, new SQLServerDialect());
+ dialect_enum_map.put(DbType.DMDB, new DmDBDialect());
+ dialect_enum_map.put(DbType.OTHER, new UnknownDialect());
+ }
+
+ public DbDialect getDialect(DbType dbType) {
+ return dialect_enum_map.get(dbType);
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DmDBDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DmDBDialect.java
new file mode 100644
index 0000000..2cc7c71
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/DmDBDialect.java
@@ -0,0 +1,61 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.exception.DataQueryException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.util.StringUtils;
+import java.sql.ResultSet;
+
+
+public class DmDBDialect extends AbstractDbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ return "select columns.column_name AS colName, columns.data_type AS DATATYPE, columns.data_length AS DATALENGTH, columns.data_precision AS DATAPRECISION, " +
+ "columns.data_scale AS DATASCALE, columns.nullable AS NULLABLE, columns.column_id AS COLPOSITION, columns.data_default AS DATADEFAULT, comments.comments AS COLCOMMENT," +
+ "case when t.column_name is null then 0 else 1 end as COLKEY " +
+ "from sys.user_tab_columns columns LEFT JOIN sys.user_col_comments comments ON columns.table_name = comments.table_name AND columns.column_name = comments.column_name " +
+ "left join ( " +
+ "select col.column_name as column_name, con.table_name as table_name from user_constraints con, user_cons_columns col " +
+ "where con.constraint_name = col.constraint_name and con.constraint_type = 'P' " +
+ ") t on t.table_name = columns.table_name and columns.column_name = t.column_name " +
+ "where columns.table_name = UPPER('" + tableName + "') order by columns.column_id ";
+ }
+
+ @Override
+ public String tables(String dbName) {
+ return "select a.table_name as TABLENAME,b.comments AS TABLECOMMENT from dba_tables a join user_tab_comments b " +
+ "on a.table_name = b.table_name where a.owner='" + dbName + "'";
+ }
+
+ @Override
+ public RowMapper columnMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setColName(rs.getString("COLNAME"));
+ entity.setDataType(rs.getString("DATATYPE"));
+ entity.setDataLength(rs.getString("DATALENGTH"));
+ entity.setDataPrecision(rs.getString("DATAPRECISION"));
+ entity.setDataScale(rs.getString("DATASCALE"));
+ entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false);
+ entity.setNullable("1".equals(rs.getString("NULLABLE")) ? true : false);
+ entity.setColPosition(rs.getInt("COLPOSITION"));
+ entity.setDataDefault(rs.getString("DATADEFAULT"));
+ entity.setColComment(rs.getString("COLCOMMENT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbTable entity = new DbTable();
+ entity.setTableName(rs.getString("TABLENAME"));
+ entity.setTableComment(rs.getString("TABLECOMMENT"));
+ return entity;
+ };
+ }
+}
+
+
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MariaDBDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MariaDBDialect.java
new file mode 100644
index 0000000..6b759a9
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MariaDBDialect.java
@@ -0,0 +1,10 @@
+package cn.datax.common.database.dialect;
+
+/**
+ * MariaDB 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class MariaDBDialect extends MySqlDialect {
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MySqlDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MySqlDialect.java
new file mode 100644
index 0000000..ff3b02f
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/MySqlDialect.java
@@ -0,0 +1,44 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+
+/**
+ * MySql 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class MySqlDialect extends AbstractDbDialect {
+
+ @Override
+ public RowMapper columnMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setColName(rs.getString("COLNAME"));
+ entity.setDataType(rs.getString("DATATYPE"));
+ entity.setDataLength(rs.getString("DATALENGTH"));
+ entity.setDataPrecision(rs.getString("DATAPRECISION"));
+ entity.setDataScale(rs.getString("DATASCALE"));
+ entity.setColKey("PRI".equals(rs.getString("COLKEY")));
+ entity.setNullable("YES".equals(rs.getString("NULLABLE")));
+ entity.setColPosition(rs.getInt("COLPOSITION"));
+ entity.setDataDefault(rs.getString("DATADEFAULT"));
+ entity.setColComment(rs.getString("COLCOMMENT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbTable entity = new DbTable();
+ entity.setTableName(rs.getString("TABLENAME"));
+ entity.setTableComment(rs.getString("TABLECOMMENT"));
+ return entity;
+ };
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/Oracle12cDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/Oracle12cDialect.java
new file mode 100644
index 0000000..410368a
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/Oracle12cDialect.java
@@ -0,0 +1,17 @@
+package cn.datax.common.database.dialect;
+
+/**
+ * ORACLE Oracle12c+数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class Oracle12cDialect extends OracleDialect {
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ StringBuilder sqlBuilder = new StringBuilder(originalSql);
+ sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY ");
+ return sqlBuilder.toString();
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/OracleDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/OracleDialect.java
new file mode 100644
index 0000000..16be903
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/OracleDialect.java
@@ -0,0 +1,85 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Oracle Oracle11g及以下数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class OracleDialect extends AbstractDbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ return "select columns.column_name AS colName, columns.data_type AS DATATYPE, columns.data_length AS DATALENGTH, columns.data_precision AS DATAPRECISION, " +
+ "columns.data_scale AS DATASCALE, columns.nullable AS NULLABLE, columns.column_id AS COLPOSITION, columns.data_default AS DATADEFAULT, comments.comments AS COLCOMMENT," +
+ "case when t.column_name is null then 0 else 1 end as COLKEY " +
+ "from sys.user_tab_columns columns LEFT JOIN sys.user_col_comments comments ON columns.table_name = comments.table_name AND columns.column_name = comments.column_name " +
+ "left join ( " +
+ "select col.column_name as column_name, con.table_name as table_name from user_constraints con, user_cons_columns col " +
+ "where con.constraint_name = col.constraint_name and con.constraint_type = 'P' " +
+ ") t on t.table_name = columns.table_name and columns.column_name = t.column_name " +
+ "where columns.table_name = UPPER('" + tableName + "') order by columns.column_id ";
+ }
+
+ @Override
+ public String tables(String dbName) {
+ return "select tables.table_name AS TABLENAME, comments.comments AS TABLECOMMENT from sys.user_tables tables " +
+ "LEFT JOIN sys.user_tab_comments comments ON tables.table_name = comments.table_name ";
+ }
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ StringBuilder sqlBuilder = new StringBuilder();
+ sqlBuilder.append("SELECT * FROM ( SELECT TMP.*, ROWNUM ROW_ID FROM ( ");
+ sqlBuilder.append(originalSql).append(" ) TMP WHERE ROWNUM <=").append((offset >= 1) ? (offset + count) : count);
+ sqlBuilder.append(") WHERE ROW_ID > ").append(offset);
+ return sqlBuilder.toString();
+ }
+
+ @Override
+ public RowMapper columnLongMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setDataDefault(rs.getString("DATADEFAULT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper columnMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setColName(rs.getString("COLNAME"));
+ entity.setDataType(rs.getString("DATATYPE"));
+ entity.setDataLength(rs.getString("DATALENGTH"));
+ entity.setDataPrecision(rs.getString("DATAPRECISION"));
+ entity.setDataScale(rs.getString("DATASCALE"));
+ entity.setColKey("1".equals(rs.getString("COLKEY")));
+ entity.setNullable("Y".equals(rs.getString("NULLABLE")));
+ //long类型,单独处理
+ //entity.setDataDefault(rs.getString("DATADEFAULT"));
+ entity.setColPosition(rs.getInt("COLPOSITION"));
+ entity.setColComment(rs.getString("COLCOMMENT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbTable entity = new DbTable();
+ entity.setTableName(rs.getString("TABLENAME"));
+ entity.setTableComment(rs.getString("TABLECOMMENT"));
+ return entity;
+ };
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/PostgreDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/PostgreDialect.java
new file mode 100644
index 0000000..088cce3
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/PostgreDialect.java
@@ -0,0 +1,71 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import org.springframework.jdbc.core.RowMapper;
+
+import java.sql.ResultSet;
+
+/**
+ * Postgre 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class PostgreDialect extends AbstractDbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ return "select col.column_name AS COLNAME, col.ordinal_position AS COLPOSITION, col.column_default AS DATADEFAULT, col.is_nullable AS NULLABLE, col.udt_name AS DATATYPE, " +
+ "col.character_maximum_length AS DATALENGTH, col.numeric_precision AS DATAPRECISION, col.numeric_scale AS DATASCALE, des.description AS COLCOMMENT, " +
+ "case when t.colname is null then 0 else 1 end as COLKEY " +
+ "from information_schema.columns col left join pg_description des on col.table_name::regclass = des.objoid and col.ordinal_position = des.objsubid " +
+ "left join ( " +
+ "select pg_attribute.attname as colname from pg_constraint inner join pg_class on pg_constraint.conrelid = pg_class.oid " +
+ "inner join pg_attribute on pg_attribute.attrelid = pg_class.oid and pg_attribute.attnum = any(pg_constraint.conkey) " +
+ "where pg_class.relname = '" + tableName + "' and pg_constraint.contype = 'p' " +
+ ") t on t.colname = col.column_name " +
+ "where col.table_catalog = '" + dbName + "' and col.table_schema = 'public' and col.table_name = '" + tableName + "' order by col.ordinal_position ";
+ }
+
+ @Override
+ public String tables(String dbName) {
+ return "select relname AS TABLENAME, cast(obj_description(relfilenode, 'pg_class') as varchar) AS TABLECOMMENT from pg_class " +
+ "where relname in (select tablename from pg_tables where schemaname = 'public' and tableowner = '" + dbName + "' and position('_2' in tablename) = 0) ";
+ }
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ StringBuilder sqlBuilder = new StringBuilder(originalSql);
+ sqlBuilder.append(" LIMIT ").append(count).append(" offset ").append(offset);
+ return sqlBuilder.toString();
+ }
+
+ @Override
+ public RowMapper columnMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setColName(rs.getString("COLNAME"));
+ entity.setDataType(rs.getString("DATATYPE"));
+ entity.setDataLength(rs.getString("DATALENGTH"));
+ entity.setDataPrecision(rs.getString("DATAPRECISION"));
+ entity.setDataScale(rs.getString("DATASCALE"));
+ entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false);
+ entity.setNullable("YES".equals(rs.getString("NULLABLE")) ? true : false);
+ entity.setColPosition(rs.getInt("COLPOSITION"));
+ entity.setDataDefault(rs.getString("DATADEFAULT"));
+ entity.setColComment(rs.getString("COLCOMMENT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbTable entity = new DbTable();
+ entity.setTableName(rs.getString("TABLENAME"));
+ entity.setTableComment(rs.getString("TABLECOMMENT"));
+ return entity;
+ };
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServer2008Dialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServer2008Dialect.java
new file mode 100644
index 0000000..025cd35
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServer2008Dialect.java
@@ -0,0 +1,106 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.exception.DataQueryException;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.util.StringUtils;
+
+import java.sql.ResultSet;
+
+/**
+ * SQLServer 2005 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class SQLServer2008Dialect extends AbstractDbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ return "select columns.name AS colName, columns.column_id AS COLPOSITION, columns.max_length AS DATALENGTH, columns.precision AS DATAPRECISION, columns.scale AS DATASCALE, " +
+ "columns.is_nullable AS NULLABLE, types.name AS DATATYPE, CAST(ep.value AS NVARCHAR(128)) AS COLCOMMENT, e.text AS DATADEFAULT, " +
+ "(select top 1 ind.is_primary_key from sys.index_columns ic left join sys.indexes ind on ic.object_id = ind.object_id and ic.index_id = ind.index_id and ind.name like 'PK_%' where ic.object_id=columns.object_id and ic.column_id=columns.column_id) AS COLKEY " +
+ "from sys.columns columns LEFT JOIN sys.types types ON columns.system_type_id = types.system_type_id " +
+ "LEFT JOIN syscomments e ON columns.default_object_id= e.id " +
+ "LEFT JOIN sys.extended_properties ep ON ep.major_id = columns.object_id AND ep.minor_id = columns.column_id AND ep.name = 'MS_Description' " +
+ "where columns.object_id = object_id('" + tableName + "') order by columns.column_id ";
+ }
+
+ @Override
+ public String tables(String dbName) {
+ return "select tables.name AS TABLENAME, CAST(ep.value AS NVARCHAR(128)) AS TABLECOMMENT " +
+ "from sys.tables tables LEFT JOIN sys.extended_properties ep ON ep.major_id = tables.object_id AND ep.minor_id = 0";
+ }
+
+ private static String getOrderByPart(String sql) {
+ String loweredString = sql.toLowerCase();
+ int orderByIndex = loweredString.indexOf("order by");
+ if (orderByIndex != -1) {
+ return sql.substring(orderByIndex);
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ StringBuilder pagingBuilder = new StringBuilder();
+ String orderby = getOrderByPart(originalSql);
+ String distinctStr = "";
+
+ String loweredString = originalSql.toLowerCase();
+ String sqlPartString = originalSql;
+ if (loweredString.trim().startsWith("select")) {
+ int index = 6;
+ if (loweredString.startsWith("select distinct")) {
+ distinctStr = "DISTINCT ";
+ index = 15;
+ }
+ sqlPartString = sqlPartString.substring(index);
+ }
+ pagingBuilder.append(sqlPartString);
+
+ // if no ORDER BY is specified use fake ORDER BY field to avoid errors
+ if (StringUtils.isEmpty(orderby)) {
+ orderby = "ORDER BY CURRENT_TIMESTAMP";
+ }
+ StringBuilder sql = new StringBuilder();
+ sql.append("WITH selectTemp AS (SELECT ").append(distinctStr).append("TOP 100 PERCENT ")
+ .append(" ROW_NUMBER() OVER (").append(orderby).append(") as __row_number__, ").append(pagingBuilder)
+ .append(") SELECT * FROM selectTemp WHERE __row_number__ BETWEEN ")
+ //FIX#299:原因:mysql中limit 10(offset,size) 是从第10开始(不包含10),;而这里用的BETWEEN是两边都包含,所以改为offset+1
+ .append(offset + 1)
+ .append(" AND ")
+ .append(offset + count).append(" ORDER BY __row_number__");
+ return sql.toString();
+ }
+
+ @Override
+ public RowMapper columnMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbColumn entity = new DbColumn();
+ entity.setColName(rs.getString("COLNAME"));
+ entity.setDataType(rs.getString("DATATYPE"));
+ entity.setDataLength(rs.getString("DATALENGTH"));
+ entity.setDataPrecision(rs.getString("DATAPRECISION"));
+ entity.setDataScale(rs.getString("DATASCALE"));
+ entity.setColKey("1".equals(rs.getString("COLKEY")) ? true : false);
+ entity.setNullable("1".equals(rs.getString("NULLABLE")) ? true : false);
+ entity.setColPosition(rs.getInt("COLPOSITION"));
+ entity.setDataDefault(rs.getString("DATADEFAULT"));
+ entity.setColComment(rs.getString("COLCOMMENT"));
+ return entity;
+ };
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ return (ResultSet rs, int rowNum) -> {
+ DbTable entity = new DbTable();
+ entity.setTableName(rs.getString("TABLENAME"));
+ entity.setTableComment(rs.getString("TABLECOMMENT"));
+ return entity;
+ };
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServerDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServerDialect.java
new file mode 100644
index 0000000..60e88a9
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/SQLServerDialect.java
@@ -0,0 +1,17 @@
+package cn.datax.common.database.dialect;
+
+/**
+ * SQLServer 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class SQLServerDialect extends SQLServer2008Dialect {
+
+ @Override
+ public String buildPaginationSql(String originalSql, long offset, long count) {
+ StringBuilder sqlBuilder = new StringBuilder(originalSql);
+ sqlBuilder.append(" OFFSET ").append(offset).append(" ROWS FETCH NEXT ").append(count).append(" ROWS ONLY ");
+ return sqlBuilder.toString();
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/UnknownDialect.java b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/UnknownDialect.java
new file mode 100644
index 0000000..1620045
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/dialect/UnknownDialect.java
@@ -0,0 +1,45 @@
+package cn.datax.common.database.dialect;
+
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.exception.DataQueryException;
+import org.springframework.jdbc.core.RowMapper;
+
+/**
+ * 未知 数据库方言
+ *
+ * @author AllDataDC
+ * @date 2022-11-14
+ */
+public class UnknownDialect extends AbstractDbDialect {
+
+ @Override
+ public String columns(String dbName, String tableName) {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+
+ @Override
+ public String tables(String dbName) {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+
+ @Override
+ public String buildPaginationSql(String sql, long offset, long count) {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+
+ @Override
+ public String count(String sql) {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+
+ @Override
+ public RowMapper columnMapper() {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+
+ @Override
+ public RowMapper tableMapper() {
+ throw new DataQueryException("不支持的数据库类型");
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/exception/DataQueryException.java b/studio/common/common-database/src/main/java/cn/datax/common/database/exception/DataQueryException.java
new file mode 100644
index 0000000..20997d4
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/exception/DataQueryException.java
@@ -0,0 +1,8 @@
+package cn.datax.common.database.exception;
+
+public class DataQueryException extends RuntimeException {
+
+ public DataQueryException(String message) {
+ super(message);
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/query/AbstractDbQueryFactory.java b/studio/common/common-database/src/main/java/cn/datax/common/database/query/AbstractDbQueryFactory.java
new file mode 100644
index 0000000..15fbc54
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/query/AbstractDbQueryFactory.java
@@ -0,0 +1,150 @@
+package cn.datax.common.database.query;
+
+import cn.datax.common.database.DbDialect;
+import cn.datax.common.database.DbQuery;
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.core.PageResult;
+import cn.datax.common.database.dialect.OracleDialect;
+import cn.datax.common.database.dialect.PostgreDialect;
+import cn.datax.common.database.dialect.DmDBDialect;
+import cn.datax.common.database.exception.DataQueryException;
+import com.zaxxer.hikari.HikariDataSource;
+import lombok.Setter;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+import java.util.Map;
+
+@Setter
+public abstract class AbstractDbQueryFactory implements DbQuery {
+
+ protected DataSource dataSource;
+
+ protected JdbcTemplate jdbcTemplate;
+
+ protected DbDialect dbDialect;
+
+ @Override
+ public Connection getConnection() {
+ try {
+ return dataSource.getConnection();
+ } catch (SQLException e) {
+ throw new DataQueryException("获取数据库连接出错");
+ }
+ }
+
+ @Override
+ public boolean valid() {
+ Connection conn = null;
+ try {
+ conn = dataSource.getConnection();
+ return conn.isValid(0);
+ } catch (SQLException e) {
+ throw new DataQueryException("检测连通性出错:"+e.getMessage());
+ } finally {
+ if (conn != null) {
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ throw new DataQueryException("关闭数据库连接出错");
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void close() {
+ if (dataSource instanceof HikariDataSource) {
+ ((HikariDataSource) dataSource).close();
+ } else {
+ throw new DataQueryException("不合法数据源类型");
+ }
+ }
+
+ @Override
+ public List getTableColumns(String dbName, String tableName) {
+ String sql = dbDialect.columns(dbName, tableName);
+ if (dbDialect instanceof OracleDialect) {
+ List longColumns = jdbcTemplate.query(sql, dbDialect.columnLongMapper());
+ List queryColumns = jdbcTemplate.query(sql, dbDialect.columnMapper());
+ for (int i = 0; i < longColumns.size(); i++) {
+ DbColumn longColumn = longColumns.get(i);
+ DbColumn otherColumn = queryColumns.get(i);
+ otherColumn.setDataDefault(longColumn.getDataDefault());
+ }
+ return queryColumns;
+ }
+ return jdbcTemplate.query(sql, dbDialect.columnMapper());
+ }
+
+ @Override
+ public List getTables(String dbName) {
+ String sql = null;
+ if (dbDialect instanceof PostgreDialect) {
+ //如果数据源为PostgreSql,同步元数据时默认owner为登录账号(userName),故不能传递dbName需传递userName
+ HikariDataSource hikariDataSource = (HikariDataSource) dataSource;
+ String username = hikariDataSource.getUsername();
+ sql = dbDialect.tables(username);
+ } else {
+ sql = dbDialect.tables(dbName);
+ }
+ return jdbcTemplate.query(sql, dbDialect.tableMapper());
+ }
+
+ @Override
+ public int count(String sql) {
+ return jdbcTemplate.queryForObject(dbDialect.count(sql), Integer.class);
+ }
+
+ @Override
+ public int count(String sql, Object[] args) {
+ return jdbcTemplate.queryForObject(dbDialect.count(sql), args, Integer.class);
+ }
+
+ @Override
+ public int count(String sql, Map params) {
+ NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+ return namedJdbcTemplate.queryForObject(dbDialect.count(sql), params, Integer.class);
+ }
+
+ @Override
+ public List> queryList(String sql) {
+ return jdbcTemplate.queryForList(sql);
+ }
+
+ @Override
+ public List> queryList(String sql, Object[] args) {
+ return jdbcTemplate.queryForList(sql, args);
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, long offset, long size) {
+ int total = count(sql);
+ String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
+ List> records = jdbcTemplate.queryForList(pageSql);
+ return new PageResult<>(total, records);
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, Object[] args, long offset, long size) {
+ int total = count(sql, args);
+ String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
+ List> records = jdbcTemplate.queryForList(pageSql, args);
+ return new PageResult<>(total, records);
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, Map params, long offset, long size) {
+ int total = count(sql, params);
+ String pageSql = dbDialect.buildPaginationSql(sql, offset, size);
+ NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+ List> records = namedJdbcTemplate.queryForList(pageSql, params);
+ return new PageResult<>(total, records);
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/query/CacheDbQueryFactoryBean.java b/studio/common/common-database/src/main/java/cn/datax/common/database/query/CacheDbQueryFactoryBean.java
new file mode 100644
index 0000000..1934863
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/query/CacheDbQueryFactoryBean.java
@@ -0,0 +1,104 @@
+package cn.datax.common.database.query;
+
+import cn.datax.common.database.cache.DefaultSqlCache;
+import cn.datax.common.database.core.DbColumn;
+import cn.datax.common.database.core.DbTable;
+import cn.datax.common.database.core.PageResult;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+public class CacheDbQueryFactoryBean extends AbstractDbQueryFactory {
+
+ /**
+ * 默认缓存5分钟
+ */
+ private static long DEFAULT_EXPIRE = 5 * 60 * 1000;
+ private static DefaultSqlCache sqlCache = new DefaultSqlCache(100, DEFAULT_EXPIRE);
+
+ private T putCacheValue(String key, T value, long ttl) {
+ sqlCache.put(key, value, ttl);
+ return value;
+ }
+
+ @Override
+ public List getTableColumns(String dbName, String tableName) {
+ Object[] args = new Object[]{dbName, tableName};
+ Optional.ofNullable(sqlCache.get(sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":getTableColumns", args)));
+ return super.getTableColumns(dbName, tableName);
+ }
+
+ @Override
+ public List getTables(String dbName) {
+ Object[] args = new Object[]{dbName};
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":getTables", args);
+ return (List) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.getTables(dbName), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public int count(String sql) {
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, null);
+ return (int) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.count(sql), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public int count(String sql, Object[] args) {
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args);
+ return (int) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.count(sql, args), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public int count(String sql, Map params) {
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, params.values().toArray());
+ return (int) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.count(sql, params), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public List> queryList(String sql) {
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, null);
+ return (List>) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.queryList(sql), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public List> queryList(String sql, Object[] args) {
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args);
+ return (List>) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.queryList(sql, args), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, long offset, long size) {
+ Object[] args = new Object[]{offset, size};
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, args);
+ return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.queryByPage(sql, offset, size), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, Object[] args, long offset, long size) {
+ Object[] objects = Arrays.copyOf(args, args.length + 2);
+ objects[args.length] = offset;
+ objects[args.length + 1] = size;
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, objects);
+ return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.queryByPage(sql, args, offset, size), DEFAULT_EXPIRE));
+ }
+
+ @Override
+ public PageResult> queryByPage(String sql, Map params, long offset, long size) {
+ Object[] args = params.values().toArray();
+ Object[] objects = Arrays.copyOf(args, args.length + 2);
+ objects[args.length] = offset;
+ objects[args.length + 1] = size;
+ String cacheKey = sqlCache.buildSqlCacheKey(super.dataSource.toString() + ":" + sql, objects);
+ return (PageResult>) Optional.ofNullable(sqlCache.get(cacheKey))
+ .orElse(putCacheValue(cacheKey, super.queryByPage(sql, params, offset, size), DEFAULT_EXPIRE));
+ }
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/query/DefaultDbQueryFactoryBean.java b/studio/common/common-database/src/main/java/cn/datax/common/database/query/DefaultDbQueryFactoryBean.java
new file mode 100644
index 0000000..7cf0849
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/query/DefaultDbQueryFactoryBean.java
@@ -0,0 +1,5 @@
+package cn.datax.common.database.query;
+
+public class DefaultDbQueryFactoryBean extends AbstractDbQueryFactory {
+
+}
diff --git a/studio/common/common-database/src/main/java/cn/datax/common/database/utils/MD5Util.java b/studio/common/common-database/src/main/java/cn/datax/common/database/utils/MD5Util.java
new file mode 100644
index 0000000..f6624cd
--- /dev/null
+++ b/studio/common/common-database/src/main/java/cn/datax/common/database/utils/MD5Util.java
@@ -0,0 +1,46 @@
+package cn.datax.common.database.utils;
+
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public class MD5Util {
+
+ public static void main(String[] args) throws InterruptedException {
+ Object[] arr = new Object[]{"dbName"};
+ Object[] objects = Arrays.copyOf(arr, arr.length + 2);
+ System.out.println(objects.length);
+ int length = arr.length;
+ objects[length] = 1;
+ objects[length+1] = 2;
+ System.out.println(Arrays.toString(objects));
+// String encrypt = MD5Util.encrypt("sql" + ":" + Arrays.toString(arr));
+// System.out.println(encrypt);
+ }
+
+ private static final char[] HEX_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * MD5加密
+ */
+ public static String encrypt(String value){
+ return encrypt(value.getBytes());
+ }
+
+ /**
+ * MD5加密
+ */
+ public static String encrypt(byte[] value){
+ try {
+ byte[] bytes = MessageDigest.getInstance("MD5").digest(value);
+ char[] chars = new char[32];
+ for (int i = 0; i < chars.length; i = i + 2) {
+ byte b = bytes[i / 2];
+ chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
+ chars[i + 1] = HEX_CHARS[b & 0xf];
+ }
+ return new String(chars);
+ } catch (Exception e) {
+ throw new RuntimeException("md5 encrypt error", e);
+ }
+ }
+}
diff --git a/studio/common/common-database/src/main/resources/META-INF/spring.factories b/studio/common/common-database/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..c1fbc75
--- /dev/null
+++ b/studio/common/common-database/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.database.datasource.CacheDataSourceFactoryBean
diff --git a/studio/common/common-dictionary/pom.xml b/studio/common/common-dictionary/pom.xml
new file mode 100644
index 0000000..dbc48a8
--- /dev/null
+++ b/studio/common/common-dictionary/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-dictionary
+
+
+
+
+ org.springframework
+ spring-webmvc
+ provided
+
+
+ com.platform
+ common-core
+ 0.4.x
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.security
+ spring-security-web
+
+
+
+
+ com.platform
+ common-redis
+ 0.4.x
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/annotation/DictAop.java b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/annotation/DictAop.java
new file mode 100644
index 0000000..fa0cb93
--- /dev/null
+++ b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/annotation/DictAop.java
@@ -0,0 +1,12 @@
+package cn.datax.common.dictionary.annotation;
+
+import java.lang.annotation.*;
+
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DictAop {
+
+ /** 字典编码 */
+ String code() default "";
+}
diff --git a/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/config/DictAnalysis.java b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/config/DictAnalysis.java
new file mode 100644
index 0000000..eb3ae37
--- /dev/null
+++ b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/config/DictAnalysis.java
@@ -0,0 +1,59 @@
+package cn.datax.common.dictionary.config;
+
+import cn.datax.common.core.JsonPage;
+import cn.datax.common.core.R;
+import cn.datax.common.dictionary.annotation.DictAop;
+import cn.datax.common.dictionary.utils.DictUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+@ControllerAdvice
+public class DictAnalysis implements ResponseBodyAdvice {
+
+ @Override
+ public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
+ if (o instanceof R) {
+ if (((R) o).getData() instanceof JsonPage) {
+ List list = ((JsonPage) ((R) o).getData()).getData();
+ List items = new ArrayList<>();
+ for (Object record : list) {
+ JSONObject item = JSONObject.parseObject(JSON.toJSONString(record));
+ for (Field field : record.getClass().getDeclaredFields()) {
+ // 获取自定义注解
+ DictAop dictAop = field.getAnnotation(DictAop.class);
+ if (null != dictAop) {
+ String code = dictAop.code();
+ String text = field.getName();
+ Object object = item.get(field.getName());
+ if (null != object) {
+ // 字典翻译
+ Object dictValue = DictUtil.getInstance().getDictItemValue(code, object.toString());
+ if (null != dictValue) {
+ item.put(field.getName() + "_dictText", dictValue);
+ }
+ }
+ }
+ }
+ items.add(item);
+ }
+ ((JsonPage) ((R) o).getData()).setData(items);
+ }
+ }
+ return o;
+ }
+
+ @Override
+ public boolean supports(MethodParameter methodParameter, Class aClass) {
+ return true;
+ }
+}
diff --git a/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/ConfigUtil.java b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/ConfigUtil.java
new file mode 100644
index 0000000..ae0b6fc
--- /dev/null
+++ b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/ConfigUtil.java
@@ -0,0 +1,39 @@
+package cn.datax.common.dictionary.utils;
+
+import cn.datax.common.core.RedisConstant;
+import cn.datax.common.redis.service.RedisService;
+import cn.datax.common.utils.SpringContextHolder;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Optional;
+
+@Slf4j
+public class ConfigUtil {
+
+ private ConfigUtil() {}
+
+ private static volatile ConfigUtil instance;
+
+ public static ConfigUtil getInstance() {
+ if(instance == null) {
+ synchronized (ConfigUtil.class) {
+ if(instance == null) {
+ instance = new ConfigUtil();
+ }
+ }
+ }
+ return instance;
+ }
+
+ private RedisService redisService = SpringContextHolder.getBean(RedisService.class);
+
+ /**
+ * 获取参数
+ * @param code
+ */
+ public Object getConfig(String code) {
+ String key = RedisConstant.SYSTEM_CONFIG_KEY;
+ Object o = redisService.hget(key, code);
+ return Optional.ofNullable(o).orElse(null);
+ }
+}
diff --git a/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/DictUtil.java b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/DictUtil.java
new file mode 100644
index 0000000..01134f8
--- /dev/null
+++ b/studio/common/common-dictionary/src/main/java/cn/datax/common/dictionary/utils/DictUtil.java
@@ -0,0 +1,69 @@
+package cn.datax.common.dictionary.utils;
+
+import cn.datax.common.core.RedisConstant;
+import cn.datax.common.redis.service.RedisService;
+import cn.datax.common.utils.SpringContextHolder;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Slf4j
+public class DictUtil {
+
+ private DictUtil() {}
+
+ private static volatile DictUtil instance;
+
+ public static DictUtil getInstance() {
+ if(instance == null) {
+ synchronized (DictUtil.class) {
+ if(instance == null) {
+ instance = new DictUtil();
+ }
+ }
+ }
+ return instance;
+ }
+
+ private RedisService redisService = SpringContextHolder.getBean(RedisService.class);
+
+ /**
+ * 获取字典项
+ * @param code
+ */
+ public Object getDictItemList(String code) {
+ String key = RedisConstant.SYSTEM_DICT_KEY;
+ Object object = redisService.get(key);
+ if (null == object) {
+ return null;
+ }
+ JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(object));
+ List list = jsonArray.stream().filter(obj -> ((JSONObject) obj).get("dictCode").equals(code))
+ .flatMap(obj -> ((JSONObject) obj).getJSONArray("items").stream()).collect(Collectors.toList());
+ return list;
+ }
+
+ /**
+ * 获取字典项值
+ * @param code
+ * @param text
+ * @return
+ */
+ public Object getDictItemValue(String code, String text) {
+ String key = RedisConstant.SYSTEM_DICT_KEY;
+ Object object = redisService.get(key);
+ if (null == object) {
+ return null;
+ }
+ JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(object));
+ Object o = jsonArray.stream().filter(obj -> ((JSONObject) obj).get("dictCode").equals(code))
+ .flatMap(obj -> ((JSONObject) obj).getJSONArray("items").stream())
+ .filter(obj -> ((JSONObject) obj).get("itemText").equals(text))
+ .map(obj -> ((JSONObject) obj).get("itemValue")).findFirst().orElse(null);
+ return o;
+ }
+}
diff --git a/studio/common/common-dictionary/src/main/resources/META-INF/spring.factories b/studio/common/common-dictionary/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..b9c3c9b
--- /dev/null
+++ b/studio/common/common-dictionary/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.dictionary.config.DictAnalysis
diff --git a/studio/common/common-jasperreport/pom.xml b/studio/common/common-jasperreport/pom.xml
new file mode 100644
index 0000000..0edc03e
--- /dev/null
+++ b/studio/common/common-jasperreport/pom.xml
@@ -0,0 +1,36 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-jasperreport
+
+
+
+ net.sf.jasperreports
+ jasperreports
+ ${jasperreports.version}
+
+
+ com.lowagie
+ itext
+
+
+
+
+ com.lowagie
+ itext
+ 2.1.7
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
diff --git a/studio/common/common-jasperreport/src/main/java/cn/datax/common/jasperreport/utils/JasperReportUtil.java b/studio/common/common-jasperreport/src/main/java/cn/datax/common/jasperreport/utils/JasperReportUtil.java
new file mode 100644
index 0000000..5e15e5c
--- /dev/null
+++ b/studio/common/common-jasperreport/src/main/java/cn/datax/common/jasperreport/utils/JasperReportUtil.java
@@ -0,0 +1,126 @@
+package cn.datax.common.jasperreport.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import net.sf.jasperreports.engine.*;
+import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
+import net.sf.jasperreports.engine.export.HtmlExporter;
+import net.sf.jasperreports.export.HtmlExporterOutput;
+import net.sf.jasperreports.export.SimpleExporterInput;
+import net.sf.jasperreports.export.SimpleHtmlExporterOutput;
+import net.sf.jasperreports.export.SimpleHtmlReportConfiguration;
+import org.springframework.core.io.ClassPathResource;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+public class JasperReportUtil {
+
+ final static String jasperDir = "jaspers";
+
+ public static String getJasperFileDir(String fileName) {
+ return jasperDir + File.separator + fileName + ".jasper";
+ }
+
+ private static String getContentType(ReportType type) {
+ String contentType;
+ switch (type) {
+ case HTML:
+ contentType = "text/html;charset=utf-8";
+ break;
+ case PDF:
+ contentType = "application/pdf";
+ break;
+ case XLS:
+ contentType = "application/vnd.ms-excel";
+ break;
+ case XLSX:
+ contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
+ break;
+ case XML:
+ contentType = "text/xml";
+ break;
+ case RTF:
+ contentType = "application/rtf";
+ break;
+ case CSV:
+ contentType = "text/plain";
+ break;
+ case DOC:
+ contentType = "application/msword";
+ break;
+ default:
+ contentType = "text/html;charset=utf-8";
+ }
+ return contentType;
+ }
+
+ static JasperPrint getJasperPrint(InputStream jasperStream, Map parameters, List> list) throws JRException {
+ JRDataSource dataSource;
+ if (null == list || list.size() == 0) {
+ dataSource = new JREmptyDataSource();
+ } else {
+ dataSource = new JRBeanCollectionDataSource(list);
+ }
+ JasperPrint jasperPrint = JasperFillManager.fillReport(jasperStream, parameters, dataSource);
+ return jasperPrint;
+ }
+
+ public static void exportToPdf(String jasperPath, Map parameters, List> list, HttpServletResponse response) throws Exception {
+ OutputStream outputStream = response.getOutputStream();
+ try {
+ ClassPathResource resource = new ClassPathResource(jasperPath);
+ response.setContentType(getContentType(ReportType.PDF));
+ InputStream jasperStream = resource.getInputStream();
+ JasperPrint jasperPrint = getJasperPrint(jasperStream, parameters, list);
+ JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
+ } catch (Exception e) {
+ log.error("读取报表异常", e);
+ outputStream.write("读取报表异常".getBytes());
+ } finally {
+ outputStream.flush();
+ outputStream.close();
+ }
+ }
+
+ public static void exportToHtml(String jasperPath, Map parameters, List> list, HttpServletResponse response) throws Exception {
+ response.setHeader("Content-type", "text/html;charset=utf-8");
+ response.setCharacterEncoding("utf-8");
+ response.setContentType(getContentType(ReportType.HTML));
+ PrintWriter out = response.getWriter();
+ HtmlExporter exporter = new HtmlExporter();
+ try {
+ ClassPathResource resource = new ClassPathResource(jasperPath);
+ InputStream jasperStream = resource.getInputStream();
+ JasperPrint jasperPrint = getJasperPrint(jasperStream, parameters, list);
+ exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
+ SimpleHtmlReportConfiguration configuration = new SimpleHtmlReportConfiguration();
+ exporter.setConfiguration(configuration);
+ HtmlExporterOutput outPut = new SimpleHtmlExporterOutput(out);
+ exporter.setExporterOutput(outPut);
+ exporter.exportReport();
+ } catch (Exception e) {
+ log.error("读取报表异常", e);
+ out.write("读取报表异常");
+ } finally {
+ out.flush();
+ out.close();
+ }
+ }
+
+ public enum ReportType {
+ HTML,
+ PDF,
+ XLS,
+ XLSX,
+ XML,
+ RTF,
+ CSV,
+ DOC
+ }
+}
diff --git a/studio/common/common-jasperreport/src/main/resources/jasperreports_extension.properties b/studio/common/common-jasperreport/src/main/resources/jasperreports_extension.properties
new file mode 100644
index 0000000..4184db2
--- /dev/null
+++ b/studio/common/common-jasperreport/src/main/resources/jasperreports_extension.properties
@@ -0,0 +1,2 @@
+net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
+net.sf.jasperreports.extension.simple.font.families.dejavu=jaspers/fonts/fonts.xml
\ No newline at end of file
diff --git a/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/fonts.xml b/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/fonts.xml
new file mode 100644
index 0000000..cac184d
--- /dev/null
+++ b/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/fonts.xml
@@ -0,0 +1,15 @@
+
+
+
+ jaspers/fonts/msyh.ttf
+ jaspers/fonts/msyh.ttf
+ jaspers/fonts/msyh.ttf
+ jaspers/fonts/msyh.ttf
+ Identity-H
+ true
+
+ '微软雅黑', Arial, Helvetica, sans-serif
+ '微软雅黑', Arial, Helvetica, sans-serif
+
+
+
diff --git a/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/msyh.ttf b/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/msyh.ttf
new file mode 100644
index 0000000..e5a3f11
Binary files /dev/null and b/studio/common/common-jasperreport/src/main/resources/jaspers/fonts/msyh.ttf differ
diff --git a/studio/common/common-log/pom.xml b/studio/common/common-log/pom.xml
new file mode 100644
index 0000000..97078df
--- /dev/null
+++ b/studio/common/common-log/pom.xml
@@ -0,0 +1,37 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-log
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ com.platform
+ common-service-api
+ 0.4.x
+ compile
+
+
+ org.springframework.security
+ spring-security-web
+ 5.2.2.RELEASE
+
+
+ com.platform
+ generic
+ 0.4.x
+ compile
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAop.java b/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAop.java
new file mode 100644
index 0000000..483ae6f
--- /dev/null
+++ b/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAop.java
@@ -0,0 +1,20 @@
+package cn.datax.common.log.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface LogAop {
+
+ /** 模块 */
+ String module() default "";
+
+ /** 描述 */
+ String value() default "";
+
+}
diff --git a/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAspect.java b/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAspect.java
new file mode 100644
index 0000000..779fa2e
--- /dev/null
+++ b/studio/common/common-log/src/main/java/cn/datax/common/log/annotation/LogAspect.java
@@ -0,0 +1,134 @@
+package cn.datax.common.log.annotation;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.platform.utils.RequestHolder;
+import cn.datax.service.system.api.dto.LogDto;
+import cn.hutool.core.util.URLUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.platform.utils.SecurityUtil;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.*;
+import org.aspectj.lang.reflect.MethodSignature;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+
+@Slf4j
+@Aspect
+public class LogAspect {
+
+// @Autowired
+// private LogServiceFeign logServiceFeign;
+
+ @Autowired
+ private ObjectMapper objectMapper;
+
+ // 配置织入点
+ @Pointcut("@annotation(cn.datax.common.log.annotation.LogAop)")
+ public void logPointCut() {}
+
+ /**
+ * 通知方法会将目标方法封装起来
+ *
+ * @param joinPoint 切点
+ */
+ @Around(value = "logPointCut()")
+ public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
+ long startTime = System.currentTimeMillis();
+ Object result = joinPoint.proceed();
+ long endTime = System.currentTimeMillis();
+ LogDto logDto = getLog();
+ logDto.setTime(String.valueOf(endTime - startTime));
+ handleLog(joinPoint, logDto);
+ return result;
+ }
+
+ /**
+ * 通知方法会在目标方法抛出异常后执行
+ *
+ * @param joinPoint
+ * @param e
+ */
+ @AfterThrowing(value = "logPointCut()", throwing = "e")
+ public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
+ LogDto logDto = getLog();
+ logDto.setExCode(e.getClass().getSimpleName()).setExMsg(e.getMessage());
+ handleLog(joinPoint, logDto);
+ }
+
+ private LogDto getLog(){
+ LogDto log = new LogDto();
+ HttpServletRequest request = RequestHolder.getHttpServletRequest();
+ log.setUserId(SecurityUtil.getUserId());
+ log.setUserName(SecurityUtil.getUserName());
+ log.setRemoteAddr(ServletUtil.getClientIP(request));
+ log.setRequestUri(URLUtil.getPath(request.getRequestURI()));
+ UserAgent ua = UserAgentUtil.parse(request.getHeader("User-Agent"));
+ log.setBrowser(ua.getBrowser().toString());
+ log.setOs(ua.getOs().toString());
+ return log;
+ }
+
+ protected void handleLog(final JoinPoint joinPoint, LogDto logDto) {
+ // 获得注解
+ LogAop logAop = getAnnotationLog(joinPoint);
+ if(null == logAop) {
+ return;
+ }
+ // 设置方法名称
+ String className = joinPoint.getTarget().getClass().getName();
+ String methodName = joinPoint.getSignature().getName();
+ logDto.setModule(logAop.module()).setTitle(logAop.value())
+ .setClassName(className).setMethodName(methodName);
+ try {
+ logDto.setParams(objectMapper.writeValueAsString(getRequestParams(joinPoint)));
+ } catch (JsonProcessingException e) {}
+ // 保存数据库
+// logServiceFeign.saveLog(logDto);
+ }
+
+ /**
+ * 是否存在注解,如果存在就获取
+ */
+ private LogAop getAnnotationLog(JoinPoint joinPoint) {
+ Signature signature = joinPoint.getSignature();
+ MethodSignature methodSignature = (MethodSignature) signature;
+ Method method = methodSignature.getMethod();
+ return null;
+ }
+
+ /**
+ * 获取入参
+ * */
+ private Map getRequestParams(JoinPoint joinPoint) {
+ Map requestParams = new HashMap<>();
+ // 参数名
+ String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
+ // 参数值
+ Object[] paramValues = joinPoint.getArgs();
+ for (int i = 0; i < paramNames.length; i++) {
+ Object value = paramValues[i];
+ // 如果是文件对象
+ if (value instanceof MultipartFile) {
+ MultipartFile file = (MultipartFile) value;
+ // 获取文件名
+ value = file.getOriginalFilename();
+ }
+ requestParams.put(paramNames[i], value);
+ }
+ return requestParams;
+ }
+}
+
diff --git a/studio/common/common-log/src/main/java/com/platform/exception/BadRequestException.java b/studio/common/common-log/src/main/java/com/platform/exception/BadRequestException.java
new file mode 100644
index 0000000..c456240
--- /dev/null
+++ b/studio/common/common-log/src/main/java/com/platform/exception/BadRequestException.java
@@ -0,0 +1,26 @@
+
+package com.platform.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ * 统一异常处理
+ */
+@Getter
+public class BadRequestException extends RuntimeException{
+
+ private Integer status = BAD_REQUEST.value();
+
+ public BadRequestException(String msg){
+ super(msg);
+ }
+
+ public BadRequestException(HttpStatus status,String msg){
+ super(msg);
+ this.status = status.value();
+ }
+}
diff --git a/studio/common/common-log/src/main/java/com/platform/utils/RequestHolder.java b/studio/common/common-log/src/main/java/com/platform/utils/RequestHolder.java
new file mode 100644
index 0000000..55ab93c
--- /dev/null
+++ b/studio/common/common-log/src/main/java/com/platform/utils/RequestHolder.java
@@ -0,0 +1,19 @@
+
+package com.platform.utils;
+
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Objects;
+
+/**
+ * 获取 HttpServletRequest
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+public class RequestHolder {
+
+ public static HttpServletRequest getHttpServletRequest() {
+ return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
+ }
+}
diff --git a/studio/common/common-log/src/main/java/com/platform/utils/SecurityUtil.java b/studio/common/common-log/src/main/java/com/platform/utils/SecurityUtil.java
new file mode 100644
index 0000000..ab00f1f
--- /dev/null
+++ b/studio/common/common-log/src/main/java/com/platform/utils/SecurityUtil.java
@@ -0,0 +1,125 @@
+package com.platform.utils;
+
+import com.platform.exception.BadRequestException;
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.datax.service.system.api.feign.UserServiceFeign;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class SecurityUtil {
+
+ @Autowired
+ private UserServiceFeign userServiceFeign;
+
+ @Autowired
+ private JwtUtil jwtUtil;
+
+ /**
+ * 获取用户
+ *
+ * @return user
+ */
+ public static JwtUserDto getDataUser() {
+ UserServiceFeign userServiceFeign = SpringContextHolder.getBean(UserServiceFeign.class);
+ return userServiceFeign.loginByUsername(getCurrentUsername());
+ }
+
+ public static String getCurrentUsername() {
+ HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ String authorization = request.getHeader("Authorization");
+ String tokenSubjectObject = String.valueOf(JwtUtil.getTokenSubjectObject(authorization));
+ if (tokenSubjectObject == null) {
+ throw new BadRequestException(HttpStatus.UNAUTHORIZED, "当前登录状态过期");
+ }
+ return tokenSubjectObject;
+
+ }
+
+
+ /**
+ * 获取用户ID
+ *
+ * @return id
+ */
+ public static String getUserId() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ return user.getUser().getId()+"";
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户部门
+ *
+ * @return id
+ */
+ public static String getUserDeptId() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ return user.getUser().getDeptId()+"";
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户名称
+ *
+ * @return username
+ */
+ public static String getUserName() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ return user.getUsername();
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户昵称
+ *
+ * @return nickname
+ */
+ public static String getNickname() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ return user.getUser().getNickName();
+ }
+ return "";
+ }
+
+ /**
+ * 获取用户角色
+ *
+ * @return username
+ */
+ public static List getUserRoleIds() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ List roles = new ArrayList<>(user.getRoles());
+ return roles;
+ }
+ return null;
+ }
+
+ /**
+ * 获取用户
+ *
+ * @return user
+ */
+ public static boolean isAdmin() {
+ JwtUserDto user = getDataUser();
+ if (user != null){
+ return user.getUser().getIsAdmin();
+ }
+ return false;
+ }
+}
diff --git a/studio/common/common-log/src/main/resources/META-INF/spring.factories b/studio/common/common-log/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..16496ab
--- /dev/null
+++ b/studio/common/common-log/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.log.annotation.LogAspect
diff --git a/studio/common/common-mybatis/pom.xml b/studio/common/common-mybatis/pom.xml
new file mode 100644
index 0000000..9355c2e
--- /dev/null
+++ b/studio/common/common-mybatis/pom.xml
@@ -0,0 +1,56 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-mybatis
+
+
+
+ mysql
+ mysql-connector-java
+
+
+ com.oracle.database.jdbc
+ ojdbc8
+ ${oracle.version}
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatis-plus.version}
+
+
+ com.baomidou
+ dynamic-datasource-spring-boot-starter
+ ${dynamic-datasource.version}
+
+
+ p6spy
+ p6spy
+ ${p6spy.version}
+
+
+ com.platform
+ common-core
+ 0.4.x
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.security
+ spring-security-web
+
+
+
+
+
+
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/annotation/DataScopeAop.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/annotation/DataScopeAop.java
new file mode 100644
index 0000000..8574116
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/annotation/DataScopeAop.java
@@ -0,0 +1,27 @@
+package cn.datax.common.mybatis.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 数据权限过滤注解
+ * 参考 https://gitee.com/cancerGit/Crown
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface DataScopeAop {
+
+ /**
+ * 表的别名
+ */
+ String alias() default "sys_user";
+ /**
+ * 表的部门字段
+ */
+ String deptScopeName() default "create_dept";
+ /**
+ * 表的用户字段
+ */
+ String userScopeName() default "create_by";
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/aspectj/DataScopeAspect.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/aspectj/DataScopeAspect.java
new file mode 100644
index 0000000..195f504
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/aspectj/DataScopeAspect.java
@@ -0,0 +1,125 @@
+package cn.datax.common.mybatis.aspectj;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+
+import cn.datax.common.base.BaseQueryParams;
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.core.DataRole;
+import cn.datax.common.core.DataUser;
+import cn.datax.common.mybatis.annotation.DataScopeAop;
+import cn.datax.common.utils.SecurityUtil;
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+
+/**
+ * 数据过滤处理(基于注解式,用于自定义sql)
+ */
+@Slf4j
+@Aspect
+public class DataScopeAspect {
+
+ // 配置织入点
+ @Pointcut("@annotation(cn.datax.common.mybatis.annotation.DataScopeAop)")
+ public void dataScopePointCut() {
+ }
+
+ @Before("dataScopePointCut()")
+ public void doBefore(JoinPoint point) {
+ handleDataScope(point);
+ }
+
+ protected void handleDataScope(final JoinPoint joinPoint) {
+ // 获得注解
+ DataScopeAop dataScope = getAnnotationLog(joinPoint);
+ if (dataScope == null) {
+ return;
+ }
+ JwtUserDto currentUser = SecurityUtil.getDataUser();
+ if (null != currentUser) {
+ // 如果是超级管理员,则不过滤数据
+// if (!currentUser.getUser().isAdmin) {
+// dataScopeFilter(joinPoint, currentUser, dataScope);
+// }
+ }
+ }
+
+// /**
+// * 数据范围过滤
+// *
+// * @param user
+// * @param dataScope
+// */
+// private void dataScopeFilter(JoinPoint joinPoint, JwtUserDto user, DataScopeAop dataScope) {
+// StringBuilder sqlString = new StringBuilder();
+// Set roles = user.getRoles();
+// if (CollUtil.isNotEmpty(roles)){
+// for (String role : roles){
+// String roleDataScope = role.getDataScope();
+// if (DataConstant.DataScope.ALL.getKey().equals(roleDataScope)) {
+// sqlString = new StringBuilder();
+// break;
+// } else if (DataConstant.DataScope.CUSTOM.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {}.{} IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) "
+// ,dataScope.alias()
+// ,dataScope.deptScopeName()
+// ,"'" + role.getId() + "'"
+// ));
+// } else if (DataConstant.DataScope.DEPT.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {}.{} = {} "
+// ,dataScope.alias()
+// ,dataScope.deptScopeName()
+// ,"'" + user.getDept() + "'"
+// ));
+// } else if (DataConstant.DataScope.DEPTANDCHILD.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {}.{} IN ( SELECT descendant FROM sys_dept_relation WHERE ancestor = {} )"
+// ,dataScope.alias()
+// ,dataScope.deptScopeName()
+// ,"'" + user.getDept() + "'"
+// ));
+// } else if (DataConstant.DataScope.SELF.getKey().equals(roleDataScope)) {
+// if (StrUtil.isNotBlank(dataScope.alias())) {
+// sqlString.append(StrUtil.format(" OR {}.{} = {} "
+// ,dataScope.alias()
+// ,dataScope.userScopeName()
+// ,user.getId()));
+// } else {
+// // 数据权限为仅本人且没有alias别名不查询任何数据
+// sqlString.append(" OR 1=0 ");
+// }
+// }
+// }
+// }
+// if (StrUtil.isNotBlank(sqlString.toString())) {
+// BaseQueryParams baseQueryParams = (BaseQueryParams) joinPoint.getArgs()[0];
+// baseQueryParams.setDataScope(" AND (" + sqlString.substring(4) + ")");
+// }
+// log.info("数据范围过滤:{}", sqlString);
+// }
+
+ /**
+ * 是否存在注解,如果存在就获取
+ */
+ private DataScopeAop getAnnotationLog(JoinPoint joinPoint) {
+ Signature signature = joinPoint.getSignature();
+ MethodSignature methodSignature = (MethodSignature) signature;
+ Method method = methodSignature.getMethod();
+
+ if (method != null) {
+ return method.getAnnotation(DataScopeAop.class);
+ }
+ return null;
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataBatisPlusConfig.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataBatisPlusConfig.java
new file mode 100644
index 0000000..21e6d85
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataBatisPlusConfig.java
@@ -0,0 +1,58 @@
+package cn.datax.common.mybatis.config;
+
+import cn.datax.common.mybatis.aspectj.DataScopeAspect;
+import cn.datax.common.mybatis.injector.DataLogicSqlInjector;
+import cn.datax.common.mybatis.interceptor.DataScopeInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.context.annotation.Bean;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+@MapperScan({"cn.datax.service.**.dao","com.platform.dts.mapper"})
+@EnableTransactionManagement
+public class DataBatisPlusConfig {
+
+ /**
+ * 分页插件
+ *
+ * @return PaginationInterceptor
+ */
+ @Bean
+ public PaginationInterceptor paginationInterceptor() {
+ return new PaginationInterceptor();
+ }
+
+ /**
+ * 数据权限插件
+ *
+ * @return DataScopeInterceptor
+ */
+ @Bean
+ public DataScopeInterceptor dataScopeInterceptor() {
+ return new DataScopeInterceptor();
+ }
+
+ /**
+ * 数据过滤处理(基于注解式)
+ *
+ * @return dataScopeAspect
+ */
+ @Bean
+ public DataScopeAspect dataScopeAspect() {
+ return new DataScopeAspect();
+ }
+
+ /**
+ * 自定义 SqlInjector
+ * 里面包含自定义的全局方法
+ */
+ @Bean
+ public DataLogicSqlInjector myLogicSqlInjector() {
+ return new DataLogicSqlInjector();
+ }
+
+ @Bean
+ public DataMetaObjectHandler dataMetaObjectHandler() {
+ return new DataMetaObjectHandler();
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataMetaObjectHandler.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataMetaObjectHandler.java
new file mode 100644
index 0000000..ab3d2b4
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/config/DataMetaObjectHandler.java
@@ -0,0 +1,34 @@
+package cn.datax.common.mybatis.config;
+
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.utils.SecurityUtil;
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import org.apache.ibatis.reflection.MetaObject;
+
+import java.time.LocalDateTime;
+
+public class DataMetaObjectHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
+ this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ this.strictInsertFill(metaObject, "status", String.class, DataConstant.EnableState.ENABLE.getKey());
+ this.strictInsertFill(metaObject, "createBy", String.class, SecurityUtil.getUserId());
+ this.strictInsertFill(metaObject, "updateBy", String.class, SecurityUtil.getUserId());
+ boolean bolCreateDept = metaObject.hasSetter("createDept");
+ if (bolCreateDept) {
+ this.strictInsertFill(metaObject, "createDept", String.class, SecurityUtil.getUserDeptId());
+ }
+ boolean bolFlowStatus = metaObject.hasSetter("flowStatus");
+ if (bolFlowStatus) {
+ this.strictInsertFill(metaObject, "flowStatus", String.class, DataConstant.AuditState.WAIT.getKey());
+ }
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ this.strictUpdateFill(metaObject, "updateBy", String.class, SecurityUtil.getUserId());
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/DataLogicSqlInjector.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/DataLogicSqlInjector.java
new file mode 100644
index 0000000..90f3dba
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/DataLogicSqlInjector.java
@@ -0,0 +1,27 @@
+package cn.datax.common.mybatis.injector;
+
+import cn.datax.common.mybatis.injector.methods.SelectListDataScope;
+import cn.datax.common.mybatis.injector.methods.SelectPageDataScope;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
+
+import java.util.List;
+
+/**
+ * 自定义 SqlInjector
+ */
+public class DataLogicSqlInjector extends DefaultSqlInjector {
+
+ /**
+ * 如果只需增加方法,保留MP自带方法
+ * 可以super.getMethodList() 再add
+ * @return
+ */
+ @Override
+ public List getMethodList(Class> mapperClass) {
+ List methodList = super.getMethodList(mapperClass);
+ methodList.add(new SelectListDataScope());
+ methodList.add(new SelectPageDataScope());
+ return methodList;
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectListDataScope.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectListDataScope.java
new file mode 100644
index 0000000..da0da9d
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectListDataScope.java
@@ -0,0 +1,21 @@
+package cn.datax.common.mybatis.injector.methods;
+
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+
+public class SelectListDataScope extends AbstractMethod {
+
+ public SelectListDataScope() {
+ }
+
+ @Override
+ public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
+ SqlMethod sqlMethod = SqlMethod.SELECT_LIST;
+ String sql = String.format(sqlMethod.getSql(), this.sqlFirst(), this.sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlComment());
+ SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
+ return this.addSelectMappedStatementForTable(mapperClass, "selectListDataScope", sqlSource, tableInfo);
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectPageDataScope.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectPageDataScope.java
new file mode 100644
index 0000000..22f88f3
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/injector/methods/SelectPageDataScope.java
@@ -0,0 +1,21 @@
+package cn.datax.common.mybatis.injector.methods;
+
+import com.baomidou.mybatisplus.core.enums.SqlMethod;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlSource;
+
+public class SelectPageDataScope extends AbstractMethod {
+
+ public SelectPageDataScope() {
+ }
+
+ @Override
+ public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
+ SqlMethod sqlMethod = SqlMethod.SELECT_PAGE;
+ String sql = String.format(sqlMethod.getSql(), this.sqlFirst(), this.sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), this.sqlWhereEntityWrapper(true, tableInfo), this.sqlComment());
+ SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sql, modelClass);
+ return this.addSelectMappedStatementForTable(mapperClass, "selectPageDataScope", sqlSource, tableInfo);
+ }
+}
diff --git a/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/interceptor/DataScopeInterceptor.java b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/interceptor/DataScopeInterceptor.java
new file mode 100644
index 0000000..c53cdff
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/java/cn/datax/common/mybatis/interceptor/DataScopeInterceptor.java
@@ -0,0 +1,162 @@
+package cn.datax.common.mybatis.interceptor;
+
+import cn.datax.common.base.DataScope;
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.core.DataRole;
+import cn.datax.common.core.DataUser;
+import cn.datax.common.utils.SecurityUtil;
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.datax.service.system.api.dto.UserLoginDto;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.plugin.*;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.SystemMetaObject;
+
+import java.sql.Connection;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * mybatis 数据权限拦截器
+ */
+@Slf4j
+@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
+public class DataScopeInterceptor extends AbstractSqlParserHandler implements Interceptor {
+
+ @Override
+ @SneakyThrows
+ public Object intercept(Invocation invocation) {
+ StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
+ MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
+ this.sqlParser(metaObject);
+ // 先判断是不是SELECT操作
+ MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
+ if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
+ return invocation.proceed();
+ }
+
+ BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
+ String originalSql = boundSql.getSql();
+ Object parameterObject = boundSql.getParameterObject();
+
+ //查找参数中包含DataScope类型的参数
+ DataScope dataScope = findDataScopeObject(parameterObject);
+
+ if (dataScope != null) {
+ // 获取当前的用户
+ JwtUserDto currentUser = SecurityUtil.getDataUser();
+ if (null != currentUser) {
+ UserLoginDto user = currentUser.getUser();
+ // 如果是超级管理员,则不过滤数据
+// if (!user.isAdmin) {
+// String sqlString = dataScopeFilter(currentUser, dataScope);
+// if (StrUtil.isNotBlank(sqlString)) {
+// originalSql = "SELECT * FROM (" + originalSql + ") TEMP_DATA_SCOPE WHERE 1=1 AND (" + sqlString.substring(4) + ")";
+// metaObject.setValue("delegate.boundSql.sql", originalSql);
+// }
+// }
+ }
+ }
+ return invocation.proceed();
+ }
+
+// /**
+// * 数据范围过滤
+// *
+// * @param user
+// * @param dataScope
+// */
+// private String dataScopeFilter(JwtUserDto user, DataScope dataScope) {
+// StringBuilder sqlString = new StringBuilder();
+// Set roles = user.getRoles();
+// if (CollUtil.isNotEmpty(roles)){
+// for (String role : roles){
+//// String roleDataScope = role.getDataScope();
+// if (DataConstant.DataScope.ALL.getKey().equals(roleDataScope)) {
+// sqlString = new StringBuilder();
+// break;
+// } else if (DataConstant.DataScope.CUSTOM.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {} IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) "
+// ,dataScope.getDeptScopeName()
+// ,"'" + role + "'"
+// ));
+// } else if (DataConstant.DataScope.DEPT.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {} = {} "
+// ,dataScope.getDeptScopeName()
+// ,"'" + user.getUser().getDept() + "'"
+// ));
+// } else if (DataConstant.DataScope.DEPTANDCHILD.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(
+// " OR {} IN ( SELECT descendant FROM sys_dept_relation WHERE ancestor = {} )"
+// ,dataScope.getDeptScopeName()
+// ,"'" + user.getUser().getDept() + "'"
+// ));
+// } else if (DataConstant.DataScope.SELF.getKey().equals(roleDataScope)) {
+// sqlString.append(StrUtil.format(" OR {} = {} "
+// ,dataScope.getUserScopeName()
+// ,"'" + user.getUser().getId() + "'"
+// ));
+// }
+// }
+// }
+// log.info("数据范围过滤:{}", sqlString);
+// return sqlString.toString();
+// }
+
+ /**
+ * 生成拦截对象的代理
+ *
+ * @param target 目标对象
+ * @return 代理对象
+ */
+ @Override
+ public Object plugin(Object target) {
+ if (target instanceof StatementHandler) {
+ return Plugin.wrap(target, this);
+ }
+ return target;
+ }
+
+ /**
+ * mybatis配置的属性
+ *
+ * @param properties mybatis配置的属性
+ */
+ @Override
+ public void setProperties(Properties properties) {
+
+ }
+
+ /**
+ * 查找参数是否包括DataScope对象
+ *
+ * @param parameterObj 参数列表
+ * @return DataScope
+ */
+ private DataScope findDataScopeObject(Object parameterObj) {
+ if (parameterObj instanceof DataScope) {
+ return (DataScope) parameterObj;
+ } else if (parameterObj instanceof Map) {
+ for (Object val : ((Map, ?>) parameterObj).values()) {
+ if (val instanceof DataScope) {
+ return (DataScope) val;
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/studio/common/common-mybatis/src/main/resources/META-INF/spring.factories b/studio/common/common-mybatis/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..bcba1c7
--- /dev/null
+++ b/studio/common/common-mybatis/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.mybatis.config.DataBatisPlusConfig
diff --git a/studio/common/common-office/lib/aspose-words-20.3.jar b/studio/common/common-office/lib/aspose-words-20.3.jar
new file mode 100644
index 0000000..4db4eb8
Binary files /dev/null and b/studio/common/common-office/lib/aspose-words-20.3.jar differ
diff --git a/studio/common/common-office/pom.xml b/studio/common/common-office/pom.xml
new file mode 100644
index 0000000..0c79adf
--- /dev/null
+++ b/studio/common/common-office/pom.xml
@@ -0,0 +1,23 @@
+
+
+ 4.0.0
+
+ 0.4.x
+ com.platform
+ common
+
+ 0.4.x
+ common-office
+
+
+ com.aspose
+ aspose-words
+ ${aspose.version}
+ system
+ ${project.basedir}/lib/aspose-words-20.3.jar
+
+
+
+
diff --git a/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MapMailMergeDataSource.java b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MapMailMergeDataSource.java
new file mode 100644
index 0000000..3d8f8c4
--- /dev/null
+++ b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MapMailMergeDataSource.java
@@ -0,0 +1,96 @@
+package cn.datax.commo.office.word;
+
+import com.aspose.words.IMailMergeDataSource;
+import com.aspose.words.ref.Ref;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class MapMailMergeDataSource implements IMailMergeDataSource {
+
+ private List> dataList;
+
+ private int index;
+
+ /**
+ * word模板中的«TableStart:tableName»«TableEnd:tableName»对应
+ */
+ private String tableName = null;
+
+ /**
+ * @param dataList 数据集
+ * @param tableName 与模板中的Name对应
+ */
+ public MapMailMergeDataSource(List> dataList, String tableName) {
+ this.dataList = dataList;
+ this.tableName = tableName;
+ index = -1;
+ }
+
+ /**
+ * @param data 单个数据集
+ * @param tableName 与模板中的Name对应
+ */
+ public MapMailMergeDataSource(Map data, String tableName) {
+ if (this.dataList == null) {
+ this.dataList = new ArrayList>();
+ this.dataList.add(data);
+ }
+ this.tableName = tableName;
+ index = -1;
+ }
+
+ /**
+ * 获取结果集总数
+ *
+ * @return
+ */
+ private int getCount() {
+ return this.dataList.size();
+ }
+
+ @Override
+ public IMailMergeDataSource getChildDataSource(String arg0)
+ throws Exception {
+ return null;
+ }
+
+ @Override
+ public String getTableName() throws Exception {
+ return this.tableName;
+ }
+
+ /**
+ * 实现接口
+ * 获取当前index指向数据行的数据
+ * 将数据存入args数组中即可
+ *
+ * @return ***返回false则不绑定数据***
+ */
+ @Override
+ public boolean getValue(String key, Ref args) throws Exception {
+ if (index < 0 || index >= this.getCount()) {
+ return false;
+ }
+ if (args != null) {
+ args.set(this.dataList.get(index).get(key));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 实现接口
+ * 判断是否还有下一条记录
+ */
+ @Override
+ public boolean moveNext() throws Exception {
+ index += 1;
+ if (index >= this.getCount()) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MergeDataSource.java b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MergeDataSource.java
new file mode 100644
index 0000000..110e654
--- /dev/null
+++ b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/MergeDataSource.java
@@ -0,0 +1,47 @@
+package cn.datax.commo.office.word;
+
+import com.aspose.words.Document;
+import com.aspose.words.MailMerge;
+
+import java.util.List;
+import java.util.Map;
+
+public class MergeDataSource {
+
+ /**
+ * word模板普通数据填充
+ * @param name
+ * @param value
+ * @param modelPath
+ * @return
+ * @throws Exception
+ */
+ public Document load(String[] name, Object[] value, String modelPath) throws Exception {
+ Document doc = new Document(modelPath);
+ // 这里可以做特殊字段处理(如:图片插入、字符对应的特殊符号[https://wenku.baidu.com/view/81b41244336c1eb91a375dcb.html])
+// DocumentBuilder builder = new DocumentBuilder(doc);
+// builder.moveToMergeField(key);
+// builder.insertImage((BufferedImage) value);
+ MailMerge merge = doc.getMailMerge();
+ merge.execute(name, value);
+ return doc;
+ }
+
+
+ /**
+ * word模板里有集合的表格填充
+ * @param name
+ * @param value
+ * @param modelPath
+ * @param dataList
+ * @return
+ * @throws Exception
+ */
+ public Document load(String[] name, Object[] value, String modelPath, List> dataList, String tableName) throws Exception {
+ Document doc = new Document(modelPath);
+ MailMerge merge = doc.getMailMerge();
+ merge.execute(name, value);
+ merge.executeWithRegions(new MapMailMergeDataSource(dataList, tableName));
+ return doc;
+ }
+}
diff --git a/studio/common/common-office/src/main/java/cn/datax/commo/office/word/WordUtil.java b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/WordUtil.java
new file mode 100644
index 0000000..ab49779
--- /dev/null
+++ b/studio/common/common-office/src/main/java/cn/datax/commo/office/word/WordUtil.java
@@ -0,0 +1,280 @@
+package cn.datax.commo.office.word;
+
+import com.aspose.words.*;
+import com.aspose.words.Shape;
+
+import java.awt.*;
+import java.io.*;
+import java.util.*;
+import java.util.List;
+
+public class WordUtil {
+
+ private WordUtil() {}
+
+ private static volatile WordUtil instance;
+
+ public static WordUtil getInstance() {
+ if(instance == null) {
+ synchronized (WordUtil.class) {
+ if(instance == null) {
+ instance = new WordUtil();
+ }
+ }
+ }
+ return instance;
+ }
+
+ /**
+ * 去除水印
+ */
+ static {
+ String license =
+ "\n" +
+ " \n" +
+ " \n" +
+ " Aspose.Cells for Java \n" +
+ " Aspose.Words for Java \n" +
+ " Aspose.Slides for Java \n" +
+ " \n" +
+ " Enterprise \n" +
+ " 20991231 \n" +
+ " 20991231 \n" +
+ " 8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7 \n" +
+ " \n" +
+ " datax \n" +
+ " ";
+ try {
+ new License().setLicense(new ByteArrayInputStream(license.getBytes("UTF-8")));
+ } catch (Exception e) {}
+ }
+
+ /**
+ * 获取文档
+ *
+ * @param fileName 模板文件 F:\模板.docx
+ * @return
+ * @throws Exception
+ */
+ public Document getDocument(String fileName) throws Exception {
+ return new Document(fileName);
+ }
+
+ /**
+ * 获取文档
+ *
+ * @param inputStream 模板文件输入流
+ * @return
+ * @throws Exception
+ */
+ public Document getDocument(InputStream inputStream) throws Exception {
+ return new Document(inputStream);
+ }
+
+ /**
+ * 普通数据模板 返回缓冲输入流
+ *
+ * @param name
+ * @param value
+ * @param modelPath 模板文件 F:\模板.docx
+ * @return 缓冲输入流 供controller层下载
+ * @throws Exception
+ */
+ public ByteArrayInputStream fillWordData(String[] name, Object[] value, String modelPath) throws Exception {
+ Document doc = new MergeDataSource().load(name, value, modelPath);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ doc.save(bos, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ return bis;
+ }
+
+ /**
+ * 普通数据模板 直接保存到指定位置
+ *
+ * @param name
+ * @param value
+ * @param modelPath 模板文件 F:\模板.docx
+ * @param destPath 保存文件 F:\测试.docx
+ * @throws Exception
+ */
+ public void fillWordData(String[] name, Object[] value, String modelPath, String destPath) throws Exception {
+ Document doc = new MergeDataSource().load(name, value, modelPath);
+ doc.save(destPath, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ }
+
+ /**
+ * 带集合的数据模板 返回缓冲输入流
+ *
+ * @param name
+ * @param value
+ * @param modelPath 模板文件 F:\模板.docx
+ * @param dataList 集合数据
+ * @param tableName 集合名称
+ * @throws Exception
+ */
+ public ByteArrayInputStream fillWordListData(String[] name, Object[] value, String modelPath, List> dataList, String tableName) throws Exception {
+ Document doc = new MergeDataSource().load(name, value, modelPath, dataList, tableName);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ doc.save(bos, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ return bis;
+ }
+
+ /**
+ * 带集合的数据模板 直接保存到指定位置
+ *
+ * @param name
+ * @param value
+ * @param modelPath 模板文件 F:\模板.docx
+ * @param destPath 保存文件 F:\测试.docx
+ * @param dataList 集合数据
+ * @param tableName 集合名称
+ * @throws Exception
+ */
+ public void fillWordListData(String[] name, Object[] value, String modelPath, String destPath, List> dataList, String tableName) throws Exception {
+ Document doc = new MergeDataSource().load(name, value, modelPath, dataList, tableName);
+ doc.save(destPath, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ }
+
+ /**
+ * word转pdf
+ * @param srcPath 文件路径 F:\\test\\审批流提交.docx
+ * @param destPath 目标路径 F:\\test\\20200420.pdf
+ * @throws Exception
+ */
+ public void word2pdf(String srcPath, String destPath) throws Exception {
+ // 转换开始前时间
+ long old = System.currentTimeMillis();
+ // 要转换的word文档的路径
+ Document doc = new Document(srcPath);
+ // 全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
+ doc.save(destPath, SaveOptions.createSaveOptions(SaveFormat.PDF));
+ // 转换结束后时间
+ long now = System.currentTimeMillis();
+ System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒");
+ }
+
+ /**
+ * 创建空文档
+ *
+ * @param destPath 文件路径 F:\\test\\审批流提交.docx
+ * @return
+ */
+ public void createWord(String destPath) throws Exception {
+ Document doc = new Document();
+ doc.save(destPath, SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ }
+
+ /**
+ * 加水印方法
+ *
+ * @param doc word文件流
+ * @param watermarkText 水印内容
+ */
+ public void insertWatermarkText(Document doc, String watermarkText) throws Exception {
+ Shape watermark = new Shape(doc, ShapeType.TEXT_PLAIN_TEXT);
+ watermark.setName("WaterMark");
+ watermark.getTextPath().setText(watermarkText);
+ watermark.getTextPath().setFontFamily("Arial");
+ watermark.setWidth(500);
+ watermark.setHeight(100);
+ watermark.setRotation(-40);
+ watermark.getFill().setColor(Color.GRAY);
+ watermark.setStrokeColor(Color.GRAY);
+ watermark.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
+ watermark.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
+ watermark.setWrapType(WrapType.NONE);
+ watermark.setVerticalAlignment(VerticalAlignment.CENTER);
+ watermark.setHorizontalAlignment(HorizontalAlignment.CENTER);
+ Paragraph watermarkPara = new Paragraph(doc);
+ watermarkPara.appendChild(watermark);
+ for (Section sect : doc.getSections()) {
+ insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_PRIMARY);
+ insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_FIRST);
+ insertWatermarkIntoHeader(watermarkPara, sect, HeaderFooterType.HEADER_EVEN);
+ }
+ }
+
+ private void insertWatermarkIntoHeader(Paragraph watermarkPara, Section sect, int headerType) throws Exception {
+ HeaderFooter header = sect.getHeadersFooters().getByHeaderFooterType(headerType);
+ if (header == null) {
+ header = new HeaderFooter(sect.getDocument(), headerType);
+ sect.getHeadersFooters().add(header);
+ }
+ header.appendChild(watermarkPara.deepClone(true));
+ }
+
+ public static void main(String[] args) throws Exception {
+// Map map = new HashMap<>();
+// map.put("companyName", "测试");
+// map.put("totalSalary", new BigDecimal("12.34"));
+// List> list = new ArrayList<>();
+// Map map1 = new HashMap<>();
+// map1.put("id", "1");
+// map1.put("name", "测试1");
+// map1.put("age", 12);
+// map1.put("sex", "男");
+// map1.put("salary", new BigDecimal("5.0"));
+// list.add(map1);
+// Map map2 = new HashMap<>();
+// map2.put("id", "2");
+// map2.put("name", "测试2");
+// map2.put("age", 14);
+// map2.put("sex", "女");
+// map2.put("salary", new BigDecimal("7.34"));
+// list.add(map2);
+// List objects1 = new ArrayList<>();
+// List objects2 = new ArrayList<>();
+// for(Map.Entry entry : map.entrySet()){
+// objects1.add(entry.getKey());
+// objects2.add(entry.getValue());
+// }
+// WordUtil.getInstance().fillWordListData(objects1.toArray(new String[objects1.size()]), objects2.toArray(new Object[objects2.size()]), "F:\\test\\模板.docx", "F:\\test\\123.docx", list, "workerList");
+// WordUtil.getInstance().word2pdf("F:\\test.docx", "F:\\20200420.pdf");
+//
+// // 用户表(子表) TableStart:UserList TableEnd:UserList
+// DataTable userTable = new DataTable("UserList");
+// userTable.getColumns().add("id");
+// userTable.getColumns().add("name");
+// userTable.getColumns().add("age");
+// userTable.getColumns().add("sex");
+// userTable.getColumns().add("salary");
+// for (int i = 1; i < 3; i++) {
+// DataRow row = userTable.newRow();
+// row.set(0, i);
+// row.set(1, "name" + i);
+// row.set(2, "age" + i);
+// row.set(3, "sex" + i);
+// row.set(4, "salary" + i);
+// userTable.getRows().add(row);
+// }
+// // 分数表(子表) TableStart:ScoreList TableEnd:ScoreList
+// DataTable scoreTable = new DataTable("ScoreList");
+// scoreTable.getColumns().add("id");
+// scoreTable.getColumns().add("uid");
+// scoreTable.getColumns().add("score");
+// for (int i = 1; i < 3; i++) {
+// DataRow row = scoreTable.newRow();
+// row.set(0, i);
+// row.set(1, i);
+// row.set(2, 10*i);
+// scoreTable.getRows().add(row);
+// }
+// // 提供数据源
+// DataSet dataSet = new DataSet();
+// dataSet.getTables().add(userTable);
+// dataSet.getTables().add(scoreTable);
+// DataRelation dataRelation = new DataRelation("UserScoreRelation", userTable.getColumns().get("id"), scoreTable.getColumns().get("uid"));
+// dataSet.getRelations().add(dataRelation);
+// // 合并模版
+// Document doc = new Document("F:\\test.docx");
+// //提供数据源
+// String[] fieldNames = new String[] {"name", "address"};
+// Object[] fieldValues = new Object[] {"张三", "陕西咸阳"};
+// //合并模版,相当于页面的渲染
+// MailMerge mailMerge = doc.getMailMerge();
+// mailMerge.execute(fieldNames, fieldValues);
+// mailMerge.executeWithRegions(dataSet);
+// doc.save("F:\\test_r.docx", SaveOptions.createSaveOptions(SaveFormat.DOCX));
+ }
+}
diff --git a/studio/common/common-qrcode/pom.xml b/studio/common/common-qrcode/pom.xml
new file mode 100644
index 0000000..35a822d
--- /dev/null
+++ b/studio/common/common-qrcode/pom.xml
@@ -0,0 +1,26 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-qrcode
+
+
+
+ com.google.zxing
+ core
+ ${zxing.version}
+
+
+ com.google.zxing
+ javase
+ ${zxing.version}
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-qrcode/src/main/java/cn/datax/common/qrcode/QrCodeUtil.java b/studio/common/common-qrcode/src/main/java/cn/datax/common/qrcode/QrCodeUtil.java
new file mode 100644
index 0000000..b8c260c
--- /dev/null
+++ b/studio/common/common-qrcode/src/main/java/cn/datax/common/qrcode/QrCodeUtil.java
@@ -0,0 +1,237 @@
+package cn.datax.common.qrcode;
+
+import java.awt.*;
+import java.awt.geom.RoundRectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.imageio.ImageIO;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.EncodeHintType;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.NotFoundException;
+import com.google.zxing.Result;
+import com.google.zxing.WriterException;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.client.j2se.MatrixToImageConfig;
+import com.google.zxing.client.j2se.MatrixToImageWriter;
+import com.google.zxing.common.BitMatrix;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 二维码 工具类
+ */
+@Slf4j
+public class QrCodeUtil {
+
+ /**
+ * 生成带图片的二维码(到destImagePath指向的File)
+ *
+ * @param content 二维码的内容
+ * @param width 二维码的宽度(px)
+ * @param height 二维码的高度(px)
+ * @param embeddedImgPath 被镶嵌的图片的地址(null表示不带logo图片)
+ * @param destImagePath 生成二维码图片的地址
+ *
+ * @return 生成的二维码文件path
+ * @throws IOException IOException
+ * @throws WriterException WriterException
+ */
+ public static String QREncode(String content, int width, int height, String embeddedImgPath, String destImagePath) throws IOException, WriterException {
+ File dest = getFile(destImagePath);
+ // 图像类型
+ String format = "jpg";
+ Map hints = new HashMap<>(4);
+ //设置UTF-8, 防止中文乱码
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+ //设置二维码四周白色区域的大小
+ hints.put(EncodeHintType.MARGIN, 1);
+ //设置二维码的容错性
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ //画二维码,记得调用multiFormatWriter.encode()时最后要带上hints参数,不然上面设置无效
+ BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
+ //开始画二维码
+ MatrixToImageWriter.writeToPath(bitMatrix, format, dest.toPath());
+ if (null != embeddedImgPath){
+ MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
+ BufferedImage bufferedImage = LogoMatrix(MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig), embeddedImgPath);
+ //输出带logo图片
+ ImageIO.write(bufferedImage, format, dest);
+ }
+ log.info("generate Qr code file {}", destImagePath);
+ return destImagePath;
+ }
+
+ /**
+ * 生成带图片的二维码(到outputStream流)
+ *
+ * @param content 二维码的内容
+ * @param width 二维码的宽度(px)
+ * @param height 二维码的高度(px)
+ * @param embeddedImgPath 被镶嵌的图片的地址(null表示不带logo图片)
+ * @param outputStream 生成二维码图片的地址
+ *
+ * @return 生成的二维码文件path
+ * @throws IOException IOException
+ * @throws WriterException WriterException
+ */
+ public static void QREncode(String content, int width, int height, String embeddedImgPath, OutputStream outputStream) throws IOException, WriterException {
+ // 图像类型
+ String format = "jpg";
+ Map hints = new HashMap<>(4);
+ //设置UTF-8, 防止中文乱码
+ hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
+ //设置二维码四周白色区域的大小
+ hints.put(EncodeHintType.MARGIN, 1);
+ //设置二维码的容错性
+ hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
+ //画二维码,记得调用multiFormatWriter.encode()时最后要带上hints参数,不然上面设置无效
+ BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
+ //开始画二维码
+ MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
+ BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
+ if (null != embeddedImgPath){
+ bufferedImage = LogoMatrix(bufferedImage, embeddedImgPath);
+ }
+ //输出带logo图片
+ boolean result = ImageIO.write(bufferedImage, format, outputStream);
+ log.info("generate Qr code file to OutputStream {}", result ? "success" : "fail");
+ }
+
+ /**
+ * 识别二维码内容信息
+ *
+ * @param file 二维码图片文件
+ *
+ * @return 二维码内容
+ * @throws NotFoundException NotFoundException
+ * @throws IOException IOException
+ */
+ public static String QRReader(File file) throws NotFoundException, IOException {
+ BufferedImage bufferedImage;
+ bufferedImage = ImageIO.read(file);
+ if (bufferedImage == null) {
+ return null;
+ }
+ String data = decodeQrCode(bufferedImage);
+ bufferedImage.flush();
+ log.info("Qr code from [{}] data is -> {}", file.getAbsolutePath(), data);
+ return data;
+ }
+
+ /**
+ * 识别二维码内容信息
+ *
+ * @param is 二维码图片文件流
+ *
+ * @return 二维码内容
+ * @throws NotFoundException NotFoundException
+ * @throws IOException IOException
+ */
+ public static String QRReader(InputStream is) throws NotFoundException, IOException {
+ BufferedImage bufferedImage;
+ bufferedImage = ImageIO.read(is);
+ if (bufferedImage == null) {
+ return null;
+ }
+ String data = decodeQrCode(bufferedImage);
+ bufferedImage.flush();
+ log.info("Qr code from InputStream data is -> {}", data);
+ return data;
+ }
+
+ // ---------------------------------------------------以下为辅助方法、辅助类-----------------------------------------
+
+ /**
+ * 获取文件(顺带创建文件夹,如果需要的话)
+ *
+ * @param filePath 文件path
+ * @return 文件对象
+ */
+ private static File getFile(String filePath) {
+ File file = new File(filePath);
+ if (!file.getParentFile().exists()) {
+ boolean result = file.getParentFile().mkdirs();
+ log.info("create directory {} {}", file.getParent(), result);
+ }
+ return file;
+ }
+
+ /**
+ * 二维码添加logo
+ *
+ * @param matrixImage 源二维码图片
+ * @param embeddedImgPath logo图片地址
+ * @return 返回带有logo的二维码图片
+ */
+ public static BufferedImage LogoMatrix(BufferedImage matrixImage, String embeddedImgPath) throws IOException{
+ /**
+ * 读取二维码图片,并构建绘图对象
+ */
+ Graphics2D g2 = matrixImage.createGraphics();
+ int matrixWidth = matrixImage.getWidth();
+ int matrixHeigh = matrixImage.getHeight();
+ /**
+ * 读取Logo图片
+ */
+ File logoFile = getFile(embeddedImgPath);
+ BufferedImage logo = ImageIO.read(logoFile);
+ // 开始绘制图片
+ g2.drawImage(logo, matrixWidth/5*2, matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5, null);
+ BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+ // 设置笔画对象
+ g2.setStroke(stroke);
+ // 指定弧度的圆角矩形
+ RoundRectangle2D.Float round = new RoundRectangle2D.Float(matrixWidth/5*2, matrixHeigh/5*2, matrixWidth/5, matrixHeigh/5, 20, 20);
+ g2.setColor(Color.white);
+ // 绘制圆弧矩形
+ g2.draw(round);
+ // 设置logo 有一道灰色边框
+ BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+ // 设置笔画对象
+ g2.setStroke(stroke2);
+ RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(matrixWidth/5*2+2, matrixHeigh/5*2+2, matrixWidth/5-4, matrixHeigh/5-4, 20, 20);
+ g2.setColor(new Color(128, 128, 128));
+ // 绘制圆弧矩形
+ g2.draw(round2);
+ g2.dispose();
+ matrixImage.flush();
+ return matrixImage;
+ }
+
+ /**
+ * 识别二维码内容信息
+ *二维码图片信息BufferedImage
+ * @param image
+ *
+ * @return 二维码内容
+ * @throws NotFoundException NotFoundException
+ */
+ private static String decodeQrCode(BufferedImage image) throws NotFoundException {
+ BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
+ BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+ HashMap hints = new HashMap<>(4);
+ hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
+ Result result = new MultiFormatReader().decode(bitmap, hints);
+ return result.getText();
+ }
+
+ public static void main(String[] args) throws IOException, WriterException, NotFoundException {
+ // 生成二维码
+// QrCodeUtil.QREncode("java开发", 500, 500, null, "F://qrcode2//二维码.jpg");
+// // 生成带图片的二维码
+// QrCodeUtil.QREncode("java开发", 500, 500, "F://qrcode2//1.jpg", "F://qrcode2//带图片的二维码.jpg");
+// // 识别二维码
+// QrCodeUtil.QRReader(new File("F://qrcode2//二维码.jpg"));
+// QrCodeUtil.decodeQrCode(new FileInputStream("F://qrcode//带文字带图片的二维码.jpg"));
+ }
+}
diff --git a/studio/common/common-rabbitmq/pom.xml b/studio/common/common-rabbitmq/pom.xml
new file mode 100644
index 0000000..0e373eb
--- /dev/null
+++ b/studio/common/common-rabbitmq/pom.xml
@@ -0,0 +1,20 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-rabbitmq
+
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-rabbitmq/src/main/java/cn/datax/common/rabbitmq/config/RabbitMqConstant.java b/studio/common/common-rabbitmq/src/main/java/cn/datax/common/rabbitmq/config/RabbitMqConstant.java
new file mode 100644
index 0000000..f2f8fa5
--- /dev/null
+++ b/studio/common/common-rabbitmq/src/main/java/cn/datax/common/rabbitmq/config/RabbitMqConstant.java
@@ -0,0 +1,29 @@
+package cn.datax.common.rabbitmq.config;
+
+public class RabbitMqConstant {
+
+ /**
+ * FANOUT类型的交换机:api发布与撤销
+ */
+ public static final String FANOUT_EXCHANGE_API = "fanout.exchange.api";
+
+ /**
+ * FANOUT类型的队列:api发布与撤销
+ */
+ public static final String FANOUT_API_QUEUE = "fanout.api.queue";
+
+ /**
+ * TOPIC类型的交换机:工作流
+ */
+ public static final String TOPIC_EXCHANGE_WORKFLOW = "topic.exchange.workflow";
+
+ /**
+ * TOPIC类型的队列:工作流
+ */
+ public static final String TOPIC_WORKFLOW_QUEUE = "topic.workflow.queue";
+
+ /**
+ * TOPIC类型的路由键:工作流 {}占位符替换
+ */
+ public static final String TOPIC_WORKFLOW_KEY = "topic.workflow.key.";
+}
diff --git a/studio/common/common-rabbitmq/src/main/resources/META-INF/spring.factories b/studio/common/common-rabbitmq/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..ab43764
--- /dev/null
+++ b/studio/common/common-rabbitmq/src/main/resources/META-INF/spring.factories
@@ -0,0 +1 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=
diff --git a/studio/common/common-redis/pom.xml b/studio/common/common-redis/pom.xml
new file mode 100644
index 0000000..d0a2a95
--- /dev/null
+++ b/studio/common/common-redis/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-redis
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ io.lettuce
+ lettuce-core
+
+
+
+
+ redis.clients
+ jedis
+
+
+ org.springframework.boot
+ spring-boot-starter-cache
+
+
+ org.apache.commons
+ commons-pool2
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+
+
+
diff --git a/studio/common/common-redis/src/main/java/cn/datax/common/redis/config/RedisConfig.java b/studio/common/common-redis/src/main/java/cn/datax/common/redis/config/RedisConfig.java
new file mode 100644
index 0000000..c9560e7
--- /dev/null
+++ b/studio/common/common-redis/src/main/java/cn/datax/common/redis/config/RedisConfig.java
@@ -0,0 +1,83 @@
+package cn.datax.common.redis.config;
+
+import cn.datax.common.redis.service.DistributedLock;
+import cn.datax.common.redis.service.RedisService;
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.*;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@ConditionalOnClass(RedisOperations.class)
+public class RedisConfig {
+
+ /**
+ * 自定义redis序列化的机制,重新定义一个ObjectMapper.防止和MVC的冲突
+ *
+ * @return
+ */
+ @Bean
+ public RedisSerializer redisSerializer() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ // null数据不返回
+ objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+ objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+ //反序列化时候遇到不匹配的属性并不抛出异常
+ objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ //序列化时候遇到空对象不抛出异常
+ objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ //反序列化的时候如果是无效子类型,不抛出异常
+ objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
+ //不使用默认的dateTime进行序列化,
+ objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
+ //使用JSR310提供的序列化类,里面包含了大量的JDK8时间序列化类
+ objectMapper.registerModule(new JavaTimeModule());
+ //启用反序列化所需的类型信息,在属性中添加@class
+ objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+ //配置null值的序列化器
+ GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, null);
+ return new GenericJackson2JsonRedisSerializer(objectMapper);
+ }
+
+ /**
+ * 实例化 RedisTemplate 对象
+ *
+ * @return
+ */
+ @Bean(name = "redisTemplate")
+ public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory, RedisSerializer redisSerializer) {
+ RedisTemplate redisTemplate = new RedisTemplate<>();
+ redisTemplate.setConnectionFactory(redisConnectionFactory);
+ redisTemplate.setDefaultSerializer(redisSerializer);
+ redisTemplate.setValueSerializer(redisSerializer);
+ redisTemplate.setHashValueSerializer(redisSerializer);
+ redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
+ redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8);
+ redisTemplate.afterPropertiesSet();
+ return redisTemplate;
+ }
+
+ @Bean
+ @ConditionalOnBean(name = "redisTemplate")
+ public RedisService redisService() {
+ return new RedisService();
+ }
+
+ @Bean
+ @ConditionalOnBean(name = "redisTemplate")
+ public DistributedLock distributedLock() {
+ return new DistributedLock();
+ }
+}
\ No newline at end of file
diff --git a/studio/common/common-redis/src/main/java/cn/datax/common/redis/serializer/DataRedisSerializer.java b/studio/common/common-redis/src/main/java/cn/datax/common/redis/serializer/DataRedisSerializer.java
new file mode 100644
index 0000000..593bd0c
--- /dev/null
+++ b/studio/common/common-redis/src/main/java/cn/datax/common/redis/serializer/DataRedisSerializer.java
@@ -0,0 +1,40 @@
+package cn.datax.common.redis.serializer;
+
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+
+import java.io.*;
+
+public class DataRedisSerializer implements RedisSerializer {
+
+ @Override
+ public byte[] serialize(Object o) throws SerializationException {
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ ObjectOutputStream objOut;
+ try {
+ objOut = new ObjectOutputStream(byteOut);
+ objOut.writeObject(o);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return byteOut.toByteArray();
+ }
+
+ @Override
+ public Object deserialize(byte[] bytes) throws SerializationException {
+ if (bytes == null) {
+ return null;
+ }
+ ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
+ ObjectInputStream objIn;
+ Object obj;
+ try {
+ objIn = new ObjectInputStream(byteIn);
+ obj = objIn.readObject();
+ } catch (IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ }
+ return obj;
+ }
+}
diff --git a/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/DistributedLock.java b/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/DistributedLock.java
new file mode 100644
index 0000000..e82f4b1
--- /dev/null
+++ b/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/DistributedLock.java
@@ -0,0 +1,60 @@
+package cn.datax.common.redis.service;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * redis分布式锁
+ */
+@Slf4j
+public class DistributedLock {
+
+ @Autowired
+ private StringRedisTemplate redisTemplate;
+
+ /**
+ * 加锁,无阻塞
+ * @param lock
+ * @param key
+ * @param expireTime 锁过期时间单位秒
+ * @return
+ */
+ public boolean tryLock(String lock, String key, Long expireTime) {
+ return this.tryLock(lock, key, expireTime, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 加锁,无阻塞
+ * @param lock
+ * @param key
+ * @param expireTime 锁过期时间
+ * @param timeUnit
+ * @return
+ */
+ public boolean tryLock(String lock, String key, Long expireTime, TimeUnit timeUnit) {
+ Boolean success = redisTemplate.opsForValue().setIfAbsent(lock, key, expireTime, timeUnit);
+ if (success == null || !success) {
+ log.info("申请锁(" + lock + "," + key + ")失败");
+ return false;
+ }
+ log.error("申请锁(" + lock + "," + key + ")成功");
+ return true;
+ }
+
+ public void unlock(String lock, String key) {
+ String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
+ RedisScript redisScript = new DefaultRedisScript<>(script, Long.class);
+ Long result = redisTemplate.execute(redisScript, Arrays.asList(lock, key));
+ if (result == null || result == 0) {
+ log.info("释放锁(" + lock + "," + key + ")失败,该锁不存在或锁已经过期");
+ } else {
+ log.info("释放锁(" + lock + "," + key + ")成功");
+ }
+ }
+}
diff --git a/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/RedisService.java b/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/RedisService.java
new file mode 100644
index 0000000..142406c
--- /dev/null
+++ b/studio/common/common-redis/src/main/java/cn/datax/common/redis/service/RedisService.java
@@ -0,0 +1,555 @@
+package cn.datax.common.redis.service;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 定义常用的 Redis操作
+ */
+public class RedisService {
+
+ @Autowired
+ private RedisTemplate redisTemplate;
+
+ /**
+ * 指定缓存失效时间
+ *
+ * @param key 键
+ * @param time 时间(秒)
+ * @return Boolean
+ */
+ public Boolean expire(String key, Long time) {
+ try {
+ if (time > 0) {
+ redisTemplate.expire(key, time, TimeUnit.SECONDS);
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 根据key获取过期时间
+ *
+ * @param key 键 不能为 null
+ * @return 时间(秒) 返回 0代表为永久有效
+ */
+ public Long getExpire(String key) {
+ return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 判断 key是否存在
+ *
+ * @param key 键
+ * @return true 存在 false不存在
+ */
+ public Boolean hasKey(String key) {
+ try {
+ return redisTemplate.hasKey(key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 删除缓存
+ *
+ * @param key 可以传一个值 或多个
+ */
+ public void del(String... key) {
+ if (key != null && key.length > 0) {
+ if (key.length == 1) {
+ redisTemplate.delete(key[0]);
+ } else {
+ redisTemplate.delete(Arrays.asList(key));
+ }
+ }
+ }
+
+ /**
+ * 普通缓存获取
+ *
+ * @param key 键
+ * @return 值
+ */
+ public Object get(String key) {
+ return key == null ? null : redisTemplate.opsForValue().get(key);
+ }
+
+ /**
+ * 普通缓存放入
+ *
+ * @param key 键
+ * @param value 值
+ * @return true成功 false失败
+ */
+ public Boolean set(String key, Object value) {
+ try {
+ redisTemplate.opsForValue().set(key, value);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 普通缓存放入并设置时间
+ *
+ * @param key 键
+ * @param value 值
+ * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
+ * @return true成功 false 失败
+ */
+ public Boolean set(String key, Object value, Long time) {
+ try {
+ if (time > 0) {
+ redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+ } else {
+ set(key, value);
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 递增
+ *
+ * @param key 键
+ * @param delta 要增加几(大于0)
+ * @return Long
+ */
+ public Long incr(String key, Long delta) {
+ if (delta < 0) {
+ throw new RuntimeException("递增因子必须大于0");
+ }
+ return redisTemplate.opsForValue().increment(key, delta);
+ }
+
+ /**
+ * 递减
+ *
+ * @param key 键
+ * @param delta 要减少几(小于0)
+ * @return Long
+ */
+ public Long decr(String key, Long delta) {
+ if (delta < 0) {
+ throw new RuntimeException("递减因子必须大于0");
+ }
+ return redisTemplate.opsForValue().increment(key, -delta);
+ }
+
+ /**
+ * HashGet
+ *
+ * @param key 键 不能为 null
+ * @param item 项 不能为 null
+ * @return 值
+ */
+ public Object hget(String key, String item) {
+ return redisTemplate.opsForHash().get(key, item);
+ }
+
+ /**
+ * 获取 hashKey对应的所有键值
+ *
+ * @param key 键
+ * @return 对应的多个键值
+ */
+ public Map hmget(String key) {
+ return redisTemplate.opsForHash().entries(key);
+ }
+
+ /**
+ * HashSet
+ *
+ * @param key 键
+ * @param map 对应多个键值
+ * @return true 成功 false 失败
+ */
+ public Boolean hmset(String key, Map map) {
+ try {
+ redisTemplate.opsForHash().putAll(key, map);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * HashSet 并设置时间
+ *
+ * @param key 键
+ * @param map 对应多个键值
+ * @param time 时间(秒)
+ * @return true成功 false失败
+ */
+ public Boolean hmset(String key, Map map, Long time) {
+ try {
+ redisTemplate.opsForHash().putAll(key, map);
+ if (time > 0) {
+ expire(key, time);
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 向一张hash表中放入数据,如果不存在将创建
+ *
+ * @param key 键
+ * @param item 项
+ * @param value 值
+ * @return true 成功 false失败
+ */
+ public Boolean hset(String key, String item, Object value) {
+ try {
+ redisTemplate.opsForHash().put(key, item, value);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 向一张hash表中放入数据,如果不存在将创建
+ *
+ * @param key 键
+ * @param item 项
+ * @param value 值
+ * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+ * @return true 成功 false失败
+ */
+ public Boolean hset(String key, String item, Object value, Long time) {
+ try {
+ redisTemplate.opsForHash().put(key, item, value);
+ if (time > 0) {
+ expire(key, time);
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 删除hash表中的值
+ *
+ * @param key 键 不能为 null
+ * @param item 项 可以使多个不能为 null
+ */
+ public void hdel(String key, Object... item) {
+ redisTemplate.opsForHash().delete(key, item);
+ }
+
+ /**
+ * 判断hash表中是否有该项的值
+ *
+ * @param key 键 不能为 null
+ * @param item 项 不能为 null
+ * @return true 存在 false不存在
+ */
+ public Boolean hHasKey(String key, String item) {
+ return redisTemplate.opsForHash().hasKey(key, item);
+ }
+
+ /**
+ * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+ *
+ * @param key 键
+ * @param item 项
+ * @param by 要增加几(大于0)
+ * @return Double
+ */
+ public Double hincr(String key, String item, Double by) {
+ return redisTemplate.opsForHash().increment(key, item, by);
+ }
+
+ /**
+ * hash递减
+ *
+ * @param key 键
+ * @param item 项
+ * @param by 要减少记(小于0)
+ * @return Double
+ */
+ public Double hdecr(String key, String item, Double by) {
+ return redisTemplate.opsForHash().increment(key, item, -by);
+ }
+
+ /**
+ * 根据 key获取 Set中的所有值
+ *
+ * @param key 键
+ * @return Set
+ */
+ public Set sGet(String key) {
+ try {
+ return redisTemplate.opsForSet().members(key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 根据value从一个set中查询,是否存在
+ *
+ * @param key 键
+ * @param value 值
+ * @return true 存在 false不存在
+ */
+ public Boolean sHasKey(String key, Object value) {
+ try {
+ return redisTemplate.opsForSet().isMember(key, value);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 将数据放入set缓存
+ *
+ * @param key 键
+ * @param values 值 可以是多个
+ * @return 成功个数
+ */
+ public Long sSet(String key, Object... values) {
+ try {
+ return redisTemplate.opsForSet().add(key, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+
+ /**
+ * 将set数据放入缓存
+ *
+ * @param key 键
+ * @param time 时间(秒)
+ * @param values 值 可以是多个
+ * @return 成功个数
+ */
+ public Long sSetAndTime(String key, Long time, Object... values) {
+ try {
+ Long count = redisTemplate.opsForSet().add(key, values);
+ if (time > 0)
+ expire(key, time);
+ return count;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+
+ /**
+ * 获取set缓存的长度
+ *
+ * @param key 键
+ * @return Long
+ */
+ public Long sGetSetSize(String key) {
+ try {
+ return redisTemplate.opsForSet().size(key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+
+ /**
+ * 移除值为value的
+ *
+ * @param key 键
+ * @param values 值 可以是多个
+ * @return 移除的个数
+ */
+ public Long setRemove(String key, Object... values) {
+ try {
+ return redisTemplate.opsForSet().remove(key, values);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+
+ /**
+ * 获取list缓存的内容
+ *
+ * @param key 键
+ * @param start 开始
+ * @param end 结束 0 到 -1代表所有值
+ * @return List
+ */
+ public List lGet(String key, int start, int end) {
+ try {
+ return redisTemplate.opsForList().range(key, start, end);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 获取list缓存的长度
+ *
+ * @param key 键
+ * @return Long
+ */
+ public Long lGetListSize(String key) {
+ try {
+ return redisTemplate.opsForList().size(key);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+
+ /**
+ * 通过索引 获取list中的值
+ *
+ * @param key 键
+ * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;
+ * index<0时,-1,表尾,-2倒数第二个元素,依次类推
+ * @return Object
+ */
+ public Object lGetIndex(String key, Long index) {
+ try {
+ return redisTemplate.opsForList().index(key, index);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ /**
+ * 将list放入缓存
+ *
+ * @param key 键
+ * @param value 值
+ * @return Boolean
+ */
+ public Boolean lSet(String key, Object value) {
+ try {
+ redisTemplate.opsForList().rightPush(key, value);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 将list放入缓存
+ *
+ * @param key 键
+ * @param value 值
+ * @param time 时间(秒)
+ * @return Boolean
+ */
+ public Boolean lSet(String key, Object value, Long time) {
+ try {
+ redisTemplate.opsForList().rightPush(key, value);
+ if (time > 0)
+ expire(key, time);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 将list放入缓存
+ *
+ * @param key 键
+ * @param value 值
+ * @return Boolean
+ */
+ public Boolean lSet(String key, List value) {
+ try {
+ redisTemplate.opsForList().rightPushAll(key, value);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 将list放入缓存
+ *
+ * @param key 键
+ * @param value 值
+ * @param time 时间(秒)
+ * @return Boolean
+ */
+ public Boolean lSet(String key, List value, Long time) {
+ try {
+ redisTemplate.opsForList().rightPushAll(key, value);
+ if (time > 0)
+ expire(key, time);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 根据索引修改list中的某条数据
+ *
+ * @param key 键
+ * @param index 索引
+ * @param value 值
+ * @return Boolean
+ */
+ public Boolean lUpdateIndex(String key, Long index, Object value) {
+ try {
+ redisTemplate.opsForList().set(key, index, value);
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 移除N个值为value
+ *
+ * @param key 键
+ * @param count 移除多少个
+ * @param value 值
+ * @return 移除的个数
+ */
+ public Long lRemove(String key, Long count, Object value) {
+ try {
+ return redisTemplate.opsForList().remove(key, count, value);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return 0L;
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/common/common-redis/src/main/resources/META-INF/spring.factories b/studio/common/common-redis/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..6ba0fa9
--- /dev/null
+++ b/studio/common/common-redis/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.redis.config.RedisConfig
diff --git a/studio/common/common-security/pom.xml b/studio/common/common-security/pom.xml
new file mode 100644
index 0000000..d79a409
--- /dev/null
+++ b/studio/common/common-security/pom.xml
@@ -0,0 +1,53 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+ 4.0.0
+ 0.4.x
+ common-security
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-oauth2
+
+
+ org.springframework.cloud
+ spring-cloud-starter-security
+
+
+
+ io.github.openfeign
+ feign-okhttp
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ com.platform
+ common-core
+ 0.4.x
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.security
+ spring-security-web
+
+
+
+
+ com.platform
+ common-redis
+ 0.4.x
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/annotation/DataInner.java b/studio/common/common-security/src/main/java/cn/datax/common/security/annotation/DataInner.java
new file mode 100644
index 0000000..6bcaae8
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/annotation/DataInner.java
@@ -0,0 +1,21 @@
+package cn.datax.common.security.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 服务调用不鉴权注解
+ *
+ * @author AllDataDC
+ * @date 2023/01/30
+ */
+@Target({ ElementType.PARAMETER, ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DataInner {
+ /**
+ * 是否AOP统一处理
+ *
+ * @return false, true
+ */
+ boolean value() default true;
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInnerAspect.java b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInnerAspect.java
new file mode 100644
index 0000000..3242abc
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInnerAspect.java
@@ -0,0 +1,26 @@
+package cn.datax.common.security.config;
+
+import cn.datax.common.security.annotation.DataInner;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+
+/**
+ * 服务间接口不鉴权处理逻辑
+ *
+ * @author AllDataDC
+ * @date 2023/01/30
+ */
+@Slf4j
+@Aspect
+public class DataSecurityInnerAspect {
+
+ @SneakyThrows
+ @Around("@annotation(dataInner)")
+ public Object around(ProceedingJoinPoint point, DataInner dataInner) {
+ log.info("feign内部接口访问 {} 不鉴权处理", point.getSignature().getName());
+ return point.proceed();
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInteceptorConfig.java b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInteceptorConfig.java
new file mode 100644
index 0000000..0e0a8d4
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityInteceptorConfig.java
@@ -0,0 +1,21 @@
+package cn.datax.common.security.config;
+
+import cn.datax.common.security.interceptor.DataServerProtectInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+public class DataSecurityInteceptorConfig implements WebMvcConfigurer {
+
+ @Bean
+ public HandlerInterceptor dataServerProtectInterceptor() {
+ return new DataServerProtectInterceptor();
+ }
+
+ @Override
+ @SuppressWarnings("all")
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(dataServerProtectInterceptor());
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityProtectConfig.java b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityProtectConfig.java
new file mode 100644
index 0000000..a1ef195
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/config/DataSecurityProtectConfig.java
@@ -0,0 +1,63 @@
+package cn.datax.common.security.config;
+
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.security.handler.DataAccessDeniedHandler;
+import cn.datax.common.security.handler.DataAuthExceptionEntryPoint;
+import feign.RequestInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
+import org.springframework.util.Base64Utils;
+
+public class DataSecurityProtectConfig {
+
+ @Bean
+ @ConditionalOnMissingBean(name = "accessDeniedHandler")
+ public DataAccessDeniedHandler accessDeniedHandler() {
+ return new DataAccessDeniedHandler();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(name = "authenticationEntryPoint")
+ public DataAuthExceptionEntryPoint authenticationEntryPoint() {
+ return new DataAuthExceptionEntryPoint();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(value = PasswordEncoder.class)
+ public PasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public DataSecurityInnerAspect dataSecurityInnerAspect() {
+ return new DataSecurityInnerAspect();
+ }
+
+ @Bean
+ public DataSecurityInteceptorConfig dataSecurityInteceptorConfig() {
+ return new DataSecurityInteceptorConfig();
+ }
+
+ @Bean
+ public RequestInterceptor oauth2FeignRequestInterceptor() {
+ return requestTemplate -> {
+ // 请求头中添加 Gateway Token
+ String gatewayToken = new String(Base64Utils.encode(DataConstant.Security.TOKENVALUE.getVal().getBytes()));
+ requestTemplate.header(DataConstant.Security.TOKENHEADER.getVal(), gatewayToken);
+ // 请求头中添加原请求头中的 Token
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ if (authentication != null) {
+ Object details = authentication.getDetails();
+ if (details instanceof OAuth2AuthenticationDetails) {
+ String authorizationToken = ((OAuth2AuthenticationDetails) details).getTokenValue();
+ requestTemplate.header(DataConstant.Security.AUTHORIZATION.getVal(), DataConstant.Security.TOKENTYPE.getVal() + authorizationToken);
+ }
+ }
+ };
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/config/MessageConverterConfig.java b/studio/common/common-security/src/main/java/cn/datax/common/security/config/MessageConverterConfig.java
new file mode 100644
index 0000000..b10751c
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/config/MessageConverterConfig.java
@@ -0,0 +1,23 @@
+package cn.datax.common.security.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.util.Collections;
+
+@Configuration
+public class MessageConverterConfig {
+
+ @Bean
+ public MappingJackson2HttpMessageConverter customMappingJackson2HttpMessageConverter() {
+ return new CustomMappingJackson2HttpMessageConverter();
+ }
+
+ static class CustomMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
+ public CustomMappingJackson2HttpMessageConverter() {
+ setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
+ }
+ }
+}
\ No newline at end of file
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/feign/FeignOkHttpConfig.java b/studio/common/common-security/src/main/java/cn/datax/common/security/feign/FeignOkHttpConfig.java
new file mode 100644
index 0000000..9ffc8a4
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/feign/FeignOkHttpConfig.java
@@ -0,0 +1,31 @@
+package cn.datax.common.security.feign;
+
+import feign.Feign;
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.cloud.openfeign.FeignAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+
+import java.util.concurrent.TimeUnit;
+
+@ConditionalOnClass(Feign.class)
+@AutoConfigureBefore(FeignAutoConfiguration.class)
+public class FeignOkHttpConfig {
+
+ @Bean
+ public OkHttpClient okHttpClient() {
+ return new OkHttpClient.Builder()
+ // 连接超时
+ .connectTimeout(60, TimeUnit.SECONDS)
+ // 读超时
+ .readTimeout(60, TimeUnit.SECONDS)
+ // 写超时
+ .writeTimeout(60, TimeUnit.SECONDS)
+ // 是否自动重连
+ .retryOnConnectionFailure(true)
+ .connectionPool(new ConnectionPool())
+ .build();
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAccessDeniedHandler.java b/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAccessDeniedHandler.java
new file mode 100644
index 0000000..a842d66
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAccessDeniedHandler.java
@@ -0,0 +1,21 @@
+package cn.datax.common.security.handler;
+
+import cn.datax.common.core.R;
+import cn.datax.common.utils.ResponseUtil;
+import org.springframework.http.MediaType;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class DataAccessDeniedHandler implements AccessDeniedHandler {
+
+ @Override
+ public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
+ ResponseUtil.makeResponse(
+ response, MediaType.APPLICATION_JSON_VALUE,
+ HttpServletResponse.SC_FORBIDDEN, R.error(HttpServletResponse.SC_FORBIDDEN, "没有权限访问该资源"));
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAuthExceptionEntryPoint.java b/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAuthExceptionEntryPoint.java
new file mode 100644
index 0000000..9e421d0
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/handler/DataAuthExceptionEntryPoint.java
@@ -0,0 +1,21 @@
+package cn.datax.common.security.handler;
+
+import cn.datax.common.core.R;
+import cn.datax.common.utils.ResponseUtil;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class DataAuthExceptionEntryPoint implements AuthenticationEntryPoint {
+
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
+ ResponseUtil.makeResponse(
+ response, MediaType.APPLICATION_JSON_VALUE,
+ HttpServletResponse.SC_UNAUTHORIZED, R.error(HttpServletResponse.SC_UNAUTHORIZED, "无效的Token"));
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/interceptor/DataServerProtectInterceptor.java b/studio/common/common-security/src/main/java/cn/datax/common/security/interceptor/DataServerProtectInterceptor.java
new file mode 100644
index 0000000..4e50083
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/interceptor/DataServerProtectInterceptor.java
@@ -0,0 +1,32 @@
+package cn.datax.common.security.interceptor;
+
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.core.R;
+import cn.datax.common.utils.ResponseUtil;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.http.MediaType;
+import org.springframework.util.Base64Utils;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class DataServerProtectInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
+ // 从请求头中获取Token
+ String token = request.getHeader(DataConstant.Security.TOKENHEADER.getVal());
+ String gatewayToken = new String(Base64Utils.encode(DataConstant.Security.TOKENVALUE.getVal().getBytes()));
+ // 校验Token的正确性
+ if (StrUtil.equals(gatewayToken, token)) {
+ return true;
+ } else {
+ ResponseUtil.makeResponse(
+ response, MediaType.APPLICATION_JSON_VALUE,
+ HttpServletResponse.SC_FORBIDDEN, R.error("请通过网关获取资源"));
+ return false;
+ }
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/model/OnlineUserDto.java b/studio/common/common-security/src/main/java/cn/datax/common/security/model/OnlineUserDto.java
new file mode 100644
index 0000000..8aad6e4
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/model/OnlineUserDto.java
@@ -0,0 +1,60 @@
+
+package cn.datax.common.security.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Date;
+
+/**
+ * 在线用户
+ * @author AllDataDC
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class OnlineUserDto {
+
+ /**
+ * 用户名
+ */
+ private String userName;
+
+ /**
+ * 昵称
+ */
+ private String nickName;
+
+ /**
+ * 岗位
+ */
+ private String dept;
+
+ /**
+ * 浏览器
+ */
+ private String browser;
+
+ /**
+ * IP
+ */
+ private String ip;
+
+ /**
+ * 地址
+ */
+ private String address;
+
+ /**
+ * token
+ */
+ private String key;
+
+ /**
+ * 登录时间
+ */
+ private Date loginTime;
+
+
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataRedisTokenServices.java b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataRedisTokenServices.java
new file mode 100644
index 0000000..9189e50
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataRedisTokenServices.java
@@ -0,0 +1,43 @@
+package cn.datax.common.security.utils;
+
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.*;
+
+import java.util.Map;
+
+@Slf4j
+public class DataRedisTokenServices implements ResourceServerTokenServices {
+ @Setter
+ private TokenStore tokenStore;
+
+ @Override
+ public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
+ OAuth2Authentication authentication = tokenStore.readAuthentication(accessToken);
+ OAuth2AccessToken token = readAccessToken(accessToken);
+ if(null == authentication || null == token){
+ throw new InvalidTokenException(accessToken);
+ }
+ DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
+ UserAuthenticationConverter userTokenConverter = new DataUserAuthenticationConverter();
+ accessTokenConverter.setUserTokenConverter(userTokenConverter);
+ Map map = accessTokenConverter.convertAccessToken(token, authentication);
+ if (map.containsKey("error")) {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("check_token returned error: " + map.get("error"));
+ }
+ throw new InvalidTokenException(accessToken);
+ } else {
+ return accessTokenConverter.extractAuthentication(map);
+ }
+ }
+
+ @Override
+ public OAuth2AccessToken readAccessToken(String accessToken) {
+ return tokenStore.readAccessToken(accessToken);
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataUserAuthenticationConverter.java b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataUserAuthenticationConverter.java
new file mode 100644
index 0000000..bdc68ae
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/DataUserAuthenticationConverter.java
@@ -0,0 +1,91 @@
+package cn.datax.common.security.utils;
+
+import cn.datax.common.core.DataConstant;
+import cn.datax.common.core.DataRole;
+import cn.datax.common.core.DataUser;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
+import org.springframework.util.StringUtils;
+
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 根据checktoken的结果转化用户信息
+ *
+ * @author AllDataDC
+ * @date 2023/01/30
+ */
+public class DataUserAuthenticationConverter implements UserAuthenticationConverter {
+
+ private static final String N_A = "N/A";
+
+ private Collection extends GrantedAuthority> defaultAuthorities;
+
+ public void setDefaultAuthorities(String[] defaultAuthorities) {
+ this.defaultAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.arrayToCommaDelimitedString(defaultAuthorities));
+ }
+
+ @Override
+ public Map convertUserAuthentication(Authentication authentication) {
+ Map response = new LinkedHashMap();
+ response.put(USERNAME, authentication.getName());
+ if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
+ response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
+ }
+ return response;
+ }
+
+ @Override
+ public Authentication extractAuthentication(Map map) {
+ if (map.containsKey(USERNAME)) {
+ Object principal = map.get(USERNAME);
+ Collection extends GrantedAuthority> authorities = this.getAuthorities(map);
+
+ String id = (String) map.get(DataConstant.UserAdditionalInfo.USERID.getKey());
+ String username = (String) map.get(DataConstant.UserAdditionalInfo.USERNAME.getKey());
+ String nickname = (String) map.get(DataConstant.UserAdditionalInfo.NICKNAME.getKey());
+
+ String dept = (String) map.get(DataConstant.UserAdditionalInfo.DEPT.getKey());
+ List roles = (List) map.get(DataConstant.UserAdditionalInfo.ROLE.getKey());
+ List posts = (List) map.get(DataConstant.UserAdditionalInfo.POST.getKey());
+
+ DataUser user = new DataUser(id, nickname, username, N_A, true
+ , true, true, true, authorities);
+ if (StrUtil.isNotBlank(dept)){
+ user.setDept(dept);
+ }
+ if (CollUtil.isNotEmpty(roles)){
+ user.setRoles(roles);
+ }
+ if (CollUtil.isNotEmpty(posts)){
+ user.setPosts(posts);
+ }
+ return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
+ } else {
+ return null;
+ }
+ }
+
+ private Collection extends GrantedAuthority> getAuthorities(Map map) {
+ if (!map.containsKey(AUTHORITIES)) {
+ return this.defaultAuthorities;
+ } else {
+ Object authorities = map.get(AUTHORITIES);
+ if (authorities instanceof String) {
+ return AuthorityUtils.commaSeparatedStringToAuthorityList((String)authorities);
+ } else if (authorities instanceof Collection) {
+ return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils.collectionToCommaDelimitedString((Collection)authorities));
+ } else {
+ throw new IllegalArgumentException("Authorities must be either a String or a Collection");
+ }
+ }
+ }
+}
diff --git a/studio/common/common-security/src/main/java/cn/datax/common/security/utils/RedisTokenStore.java b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/RedisTokenStore.java
new file mode 100644
index 0000000..024d5b1
--- /dev/null
+++ b/studio/common/common-security/src/main/java/cn/datax/common/security/utils/RedisTokenStore.java
@@ -0,0 +1,382 @@
+package cn.datax.common.security.utils;
+
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.security.oauth2.common.ExpiringOAuth2RefreshToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2RefreshToken;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator;
+import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.redis.JdkSerializationStrategy;
+import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStoreSerializationStrategy;
+
+import java.util.*;
+
+public class RedisTokenStore implements TokenStore {
+ private static final String ACCESS = "access:";
+ private static final String AUTH_TO_ACCESS = "auth_to_access:";
+ private static final String AUTH = "online-token-";
+ private static final String REFRESH_AUTH = "refresh_auth:";
+ private static final String ACCESS_TO_REFRESH = "access_to_refresh:";
+ private static final String REFRESH = "refresh:";
+ private static final String REFRESH_TO_ACCESS = "refresh_to_access:";
+ private static final String CLIENT_ID_TO_ACCESS = "client_id_to_access:";
+ private static final String UNAME_TO_ACCESS = "uname_to_access:";
+ private final RedisConnectionFactory connectionFactory;
+ private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
+ private RedisTokenStoreSerializationStrategy serializationStrategy = new JdkSerializationStrategy();
+ private String prefix = "";
+
+ public RedisTokenStore(RedisConnectionFactory connectionFactory) {
+ this.connectionFactory = connectionFactory;
+ }
+
+ public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
+ this.authenticationKeyGenerator = authenticationKeyGenerator;
+ }
+
+ public void setSerializationStrategy(RedisTokenStoreSerializationStrategy serializationStrategy) {
+ this.serializationStrategy = serializationStrategy;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ private RedisConnection getConnection() {
+ return this.connectionFactory.getConnection();
+ }
+
+ private byte[] serialize(Object object) {
+ return this.serializationStrategy.serialize(object);
+ }
+
+ private byte[] serializeKey(String object) {
+ return this.serialize(this.prefix + object);
+ }
+
+ private OAuth2AccessToken deserializeAccessToken(byte[] bytes) {
+ return (OAuth2AccessToken)this.serializationStrategy.deserialize(bytes, OAuth2AccessToken.class);
+ }
+
+ private OAuth2Authentication deserializeAuthentication(byte[] bytes) {
+ return (OAuth2Authentication)this.serializationStrategy.deserialize(bytes, OAuth2Authentication.class);
+ }
+
+ private OAuth2RefreshToken deserializeRefreshToken(byte[] bytes) {
+ return (OAuth2RefreshToken)this.serializationStrategy.deserialize(bytes, OAuth2RefreshToken.class);
+ }
+
+ private byte[] serialize(String string) {
+ return this.serializationStrategy.serialize(string);
+ }
+
+ private String deserializeString(byte[] bytes) {
+ return this.serializationStrategy.deserializeString(bytes);
+ }
+
+ @Override
+ public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
+ String key = this.authenticationKeyGenerator.extractKey(authentication);
+ byte[] serializedKey = this.serializeKey(AUTH_TO_ACCESS + key);
+ byte[] bytes = null;
+ RedisConnection conn = this.getConnection();
+ try {
+ bytes = conn.get(serializedKey);
+ } finally {
+ conn.close();
+ }
+ OAuth2AccessToken accessToken = this.deserializeAccessToken(bytes);
+ if (accessToken != null) {
+ OAuth2Authentication storedAuthentication = this.readAuthentication(accessToken.getValue());
+ if (storedAuthentication == null || !key.equals(this.authenticationKeyGenerator.extractKey(storedAuthentication))) {
+ this.storeAccessToken(accessToken, authentication);
+ }
+ }
+ return accessToken;
+ }
+
+ @Override
+ public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
+ return this.readAuthentication(token.getValue());
+ }
+
+ @Override
+ public OAuth2Authentication readAuthentication(String token) {
+ byte[] bytes = null;
+ RedisConnection conn = this.getConnection();
+ try {
+ bytes = conn.get(this.serializeKey("online-token-" + token));
+ } finally {
+ conn.close();
+ }
+ OAuth2Authentication auth = this.deserializeAuthentication(bytes);
+ return auth;
+ }
+
+ @Override
+ public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
+ return this.readAuthenticationForRefreshToken(token.getValue());
+ }
+
+ public OAuth2Authentication readAuthenticationForRefreshToken(String token) {
+ RedisConnection conn = getConnection();
+ try {
+ byte[] bytes = conn.get(serializeKey(REFRESH_AUTH + token));
+ OAuth2Authentication auth = deserializeAuthentication(bytes);
+ return auth;
+ } finally {
+ conn.close();
+ }
+ }
+
+ @Override
+ public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
+ byte[] serializedAccessToken = serialize(token);
+ byte[] serializedAuth = serialize(authentication);
+ byte[] accessKey = serializeKey(ACCESS + token.getValue());
+ byte[] authKey = serializeKey(AUTH + token.getValue());
+ byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + authenticationKeyGenerator.extractKey(authentication));
+ byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
+ byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
+
+ RedisConnection conn = getConnection();
+ try {
+ conn.openPipeline();
+ conn.stringCommands().set(accessKey, serializedAccessToken);
+ conn.stringCommands().set(authKey, serializedAuth);
+ conn.stringCommands().set(authToAccessKey, serializedAccessToken);
+ if (!authentication.isClientOnly()) {
+ conn.rPush(approvalKey, serializedAccessToken);
+ }
+ conn.rPush(clientId, serializedAccessToken);
+ if (token.getExpiration() != null) {
+ int seconds = token.getExpiresIn();
+ conn.expire(accessKey, seconds);
+ conn.expire(authKey, seconds);
+ conn.expire(authToAccessKey, seconds);
+ conn.expire(clientId, seconds);
+ conn.expire(approvalKey, seconds);
+ }
+ OAuth2RefreshToken refreshToken = token.getRefreshToken();
+ if (refreshToken != null && refreshToken.getValue() != null) {
+ byte[] refresh = serialize(token.getRefreshToken().getValue());
+ byte[] auth = serialize(token.getValue());
+ byte[] refreshToAccessKey = serializeKey(REFRESH_TO_ACCESS + token.getRefreshToken().getValue());
+ conn.stringCommands().set(refreshToAccessKey, auth);
+ byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + token.getValue());
+ conn.stringCommands().set(accessToRefreshKey, refresh);
+ if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
+ ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
+ Date expiration = expiringRefreshToken.getExpiration();
+ if (expiration != null) {
+ int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
+ .intValue();
+ conn.expire(refreshToAccessKey, seconds);
+ conn.expire(accessToRefreshKey, seconds);
+ }
+ }
+ }
+ conn.closePipeline();
+ } finally {
+ conn.close();
+ }
+ }
+
+ private static String getApprovalKey(OAuth2Authentication authentication) {
+ String userName = authentication.getUserAuthentication() == null ? "": authentication.getUserAuthentication().getName();
+ return getApprovalKey(authentication.getOAuth2Request().getClientId(), userName);
+ }
+
+ private static String getApprovalKey(String clientId, String userName) {
+ return clientId + (userName == null ? "" : ":" + userName);
+ }
+
+ @Override
+ public void removeAccessToken(OAuth2AccessToken accessToken) {
+ this.removeAccessToken(accessToken.getValue());
+ }
+
+ @Override
+ public OAuth2AccessToken readAccessToken(String tokenValue) {
+ byte[] key = serializeKey(ACCESS + tokenValue);
+ byte[] bytes = null;
+ RedisConnection conn = getConnection();
+ try {
+ bytes = conn.get(key);
+ } finally {
+ conn.close();
+ }
+ OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
+ return accessToken;
+ }
+
+ public void removeAccessToken(String tokenValue) {
+ byte[] accessKey = serializeKey(ACCESS + tokenValue);
+ byte[] authKey = serializeKey(AUTH + tokenValue);
+ byte[] accessToRefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
+ RedisConnection conn = getConnection();
+ try {
+ conn.openPipeline();
+ conn.get(accessKey);
+ conn.get(authKey);
+ conn.del(accessKey);
+ conn.del(accessToRefreshKey);
+ // Don't remove the refresh token - it's up to the caller to do that
+ conn.del(authKey);
+ List results = conn.closePipeline();
+ byte[] access = (byte[]) results.get(0);
+ byte[] auth = (byte[]) results.get(1);
+
+ OAuth2Authentication authentication = deserializeAuthentication(auth);
+ if (authentication != null) {
+ String key = authenticationKeyGenerator.extractKey(authentication);
+ byte[] authToAccessKey = serializeKey(AUTH_TO_ACCESS + key);
+ byte[] unameKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(authentication));
+ byte[] clientId = serializeKey(CLIENT_ID_TO_ACCESS + authentication.getOAuth2Request().getClientId());
+ conn.openPipeline();
+ conn.del(authToAccessKey);
+ conn.lRem(unameKey, 1, access);
+ conn.lRem(clientId, 1, access);
+ conn.del(serialize(ACCESS + key));
+ conn.closePipeline();
+ }
+ } finally {
+ conn.close();
+ }
+ }
+
+ @Override
+ public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
+ byte[] refreshKey = serializeKey(REFRESH + refreshToken.getValue());
+ byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + refreshToken.getValue());
+ byte[] serializedRefreshToken = serialize(refreshToken);
+ RedisConnection conn = getConnection();
+ try {
+ conn.openPipeline();
+ conn.stringCommands().set(refreshKey, serializedRefreshToken);
+ conn.stringCommands().set(refreshAuthKey, serialize(authentication));
+ if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
+ ExpiringOAuth2RefreshToken expiringRefreshToken = (ExpiringOAuth2RefreshToken) refreshToken;
+ Date expiration = expiringRefreshToken.getExpiration();
+ if (expiration != null) {
+ int seconds = Long.valueOf((expiration.getTime() - System.currentTimeMillis()) / 1000L)
+ .intValue();
+ conn.expire(refreshKey, seconds);
+ conn.expire(refreshAuthKey, seconds);
+ }
+ }
+ conn.closePipeline();
+ } finally {
+ conn.close();
+ }
+ }
+
+ @Override
+ public OAuth2RefreshToken readRefreshToken(String tokenValue) {
+ byte[] key = serializeKey(REFRESH + tokenValue);
+ byte[] bytes = null;
+ RedisConnection conn = getConnection();
+ try {
+ bytes = conn.get(key);
+ } finally {
+ conn.close();
+ }
+ OAuth2RefreshToken refreshToken = deserializeRefreshToken(bytes);
+ return refreshToken;
+ }
+
+ @Override
+ public void removeRefreshToken(OAuth2RefreshToken refreshToken) {
+ this.removeRefreshToken(refreshToken.getValue());
+ }
+
+ public void removeRefreshToken(String tokenValue) {
+ byte[] refreshKey = serializeKey(REFRESH + tokenValue);
+ byte[] refreshAuthKey = serializeKey(REFRESH_AUTH + tokenValue);
+ byte[] refresh2AccessKey = serializeKey(REFRESH_TO_ACCESS + tokenValue);
+ byte[] access2RefreshKey = serializeKey(ACCESS_TO_REFRESH + tokenValue);
+ RedisConnection conn = getConnection();
+ try {
+ conn.openPipeline();
+ conn.del(refreshKey);
+ conn.del(refreshAuthKey);
+ conn.del(refresh2AccessKey);
+ conn.del(access2RefreshKey);
+ conn.closePipeline();
+ } finally {
+ conn.close();
+ }
+ }
+
+ @Override
+ public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
+ this.removeAccessTokenUsingRefreshToken(refreshToken.getValue());
+ }
+
+ private void removeAccessTokenUsingRefreshToken(String refreshToken) {
+ byte[] key = serializeKey(REFRESH_TO_ACCESS + refreshToken);
+ List results = null;
+ RedisConnection conn = getConnection();
+ try {
+ conn.openPipeline();
+ conn.get(key);
+ conn.del(key);
+ results = conn.closePipeline();
+ } finally {
+ conn.close();
+ }
+ if (results == null) {
+ return;
+ }
+ byte[] bytes = (byte[]) results.get(0);
+ String accessToken = deserializeString(bytes);
+ if (accessToken != null) {
+ removeAccessToken(accessToken);
+ }
+ }
+
+ @Override
+ public Collection findTokensByClientIdAndUserName(String clientId, String userName) {
+ byte[] approvalKey = serializeKey(UNAME_TO_ACCESS + getApprovalKey(clientId, userName));
+ List byteList = null;
+ RedisConnection conn = getConnection();
+ try {
+ byteList = conn.lRange(approvalKey, 0, -1);
+ } finally {
+ conn.close();
+ }
+ if (byteList == null || byteList.size() == 0) {
+ return Collections. emptySet();
+ }
+ List accessTokens = new ArrayList(byteList.size());
+ for (byte[] bytes : byteList) {
+ OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
+ accessTokens.add(accessToken);
+ }
+ return Collections. unmodifiableCollection(accessTokens);
+ }
+
+ @Override
+ public Collection findTokensByClientId(String clientId) {
+ byte[] key = serializeKey(CLIENT_ID_TO_ACCESS + clientId);
+ List byteList = null;
+ RedisConnection conn = getConnection();
+ try {
+ byteList = conn.lRange(key, 0, -1);
+ } finally {
+ conn.close();
+ }
+ if (byteList == null || byteList.size() == 0) {
+ return Collections. emptySet();
+ }
+ List accessTokens = new ArrayList(byteList.size());
+ for (byte[] bytes : byteList) {
+ OAuth2AccessToken accessToken = deserializeAccessToken(bytes);
+ accessTokens.add(accessToken);
+ }
+ return Collections. unmodifiableCollection(accessTokens);
+ }
+}
diff --git a/studio/common/common-security/src/main/resources/META-INF/spring.factories b/studio/common/common-security/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..e02425a
--- /dev/null
+++ b/studio/common/common-security/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ cn.datax.common.security.feign.FeignOkHttpConfig
diff --git a/studio/common/common-service-api/pom.xml b/studio/common/common-service-api/pom.xml
new file mode 100644
index 0000000..39012d8
--- /dev/null
+++ b/studio/common/common-service-api/pom.xml
@@ -0,0 +1,46 @@
+
+
+
+ common
+ com.platform
+ 0.4.x
+
+
+ 4.0.0
+ 0.4.x
+ common-service-api
+
+
+
+ com.github.xiaoymin
+ knife4j-micro-spring-boot-starter
+ ${knife4j.version}
+
+
+
+ io.github.openfeign
+ feign-okhttp
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+ com.baomidou
+ mybatis-plus-annotation
+ 3.3.2
+
+
+ org.springframework.security
+ spring-security-core
+
+
+ com.alibaba
+ fastjson
+ 1.2.49
+
+
+
+
\ No newline at end of file
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseEntity.java
new file mode 100644
index 0000000..e866876
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseEntity.java
@@ -0,0 +1,58 @@
+package cn.datax.common.base;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public abstract class BaseEntity implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "create_time", fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+
+ /**
+ * 创建人
+ */
+ @TableField(value = "create_by", fill = FieldFill.INSERT)
+ private String createBy;
+
+ /**
+ * 更新时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "update_time", fill = FieldFill.INSERT_UPDATE)
+ private LocalDateTime updateTime;
+
+ /**
+ * 更新人
+ */
+ @TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
+ private String updateBy;
+
+ /**
+ * 状态(0不启用,1启用)
+ */
+ @TableField(value = "status", fill = FieldFill.INSERT)
+ private String status;
+
+ /**
+ * 备注
+ */
+ @TableField(value = "remark")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseQueryParams.java b/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseQueryParams.java
new file mode 100644
index 0000000..0f3d159
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/common/base/BaseQueryParams.java
@@ -0,0 +1,29 @@
+package cn.datax.common.base;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class BaseQueryParams implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ // 关键字
+ private String keyword;
+ // 当前页码
+ private Integer pageNum = 1;
+ // 分页条数
+ private Integer pageSize = 20;
+ // 排序
+ private List orderList;
+ // 数据权限
+ private String dataScope;
+
+ @Data
+ public class OrderItem{
+ private String column;
+ private boolean asc;
+ }
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/common/validate/ValidationGroups.java b/studio/common/common-service-api/src/main/java/cn/datax/common/validate/ValidationGroups.java
new file mode 100644
index 0000000..a95441a
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/common/validate/ValidationGroups.java
@@ -0,0 +1,12 @@
+package cn.datax.common.validate;
+
+public class ValidationGroups {
+
+ public interface Insert{};
+
+ public interface Update{};
+
+ public interface Status{};
+
+ public interface Other{};
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/AuthorityDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/AuthorityDto.java
new file mode 100644
index 0000000..802adf2
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/AuthorityDto.java
@@ -0,0 +1,20 @@
+
+package cn.datax.service.system.api.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * 避免序列化问题
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class AuthorityDto implements GrantedAuthority {
+
+ private String authority;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/BaseDTO.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/BaseDTO.java
new file mode 100644
index 0000000..93cfd72
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/BaseDTO.java
@@ -0,0 +1,41 @@
+package cn.datax.service.system.api.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.sql.Timestamp;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27 20:48:53
+ */
+@Getter
+@Setter
+public class BaseDTO implements Serializable {
+
+ private String createBy;
+
+ private String updateBy;
+
+ private Timestamp createTime;
+
+ private Timestamp updateTime;
+
+ @Override
+ public String toString() {
+ ToStringBuilder builder = new ToStringBuilder(this);
+ Field[] fields = this.getClass().getDeclaredFields();
+ try {
+ for (Field f : fields) {
+ f.setAccessible(true);
+ builder.append(f.getName(), f.get(this)).append("\n");
+ }
+ } catch (Exception e) {
+ builder.append("toString builder encounter an error");
+ }
+ return builder.toString();
+ }
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/ConfigDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/ConfigDto.java
new file mode 100644
index 0000000..2a82b4e
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/ConfigDto.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+/**
+ *
+ * 系统参数配置信息表 实体DTO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-19
+ */
+@ApiModel(value = "系统参数配置信息表Model")
+@Data
+public class ConfigDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+ @ApiModelProperty(value = "参数名称")
+ @NotBlank(message = "参数名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String configName;
+ @ApiModelProperty(value = "参数键名")
+ @NotBlank(message = "参数键名不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String configKey;
+ @ApiModelProperty(value = "参数键值")
+ @NotBlank(message = "参数键值不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String configValue;
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptDto.java
new file mode 100644
index 0000000..2c3b426
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptDto.java
@@ -0,0 +1,40 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@ApiModel(value = "部门Model")
+@Data
+public class DeptDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+
+ @ApiModelProperty(value = "父部门ID")
+ @NotBlank(message = "父部门ID不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String parentId;
+
+ @ApiModelProperty(value = "部门名称")
+ @NotBlank(message = "部门名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String deptName;
+
+ @ApiModelProperty(value = "部门编码")
+ @NotBlank(message = "部门编码不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String deptNo;
+
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptSmallDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptSmallDto.java
new file mode 100644
index 0000000..61083cc
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DeptSmallDto.java
@@ -0,0 +1,18 @@
+
+package cn.datax.service.system.api.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27 16:32:18
+*/
+@Data
+public class DeptSmallDto implements Serializable {
+
+ private Long id;
+
+ private String name;
+}
\ No newline at end of file
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictDto.java
new file mode 100644
index 0000000..794f7d7
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictDto.java
@@ -0,0 +1,39 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+/**
+ *
+ * 字典编码信息表 实体DTO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@ApiModel(value = "字典编码信息表Model")
+@Data
+public class DictDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+ @ApiModelProperty(value = "字典名称")
+ @NotBlank(message = "字典名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String dictName;
+ @ApiModelProperty(value = "字典编码")
+ @NotBlank(message = "字典编码不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String dictCode;
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictItemDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictItemDto.java
new file mode 100644
index 0000000..38ece6c
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictItemDto.java
@@ -0,0 +1,45 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+/**
+ *
+ * 字典项信息表 实体DTO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@ApiModel(value = "字典项信息表Model")
+@Data
+public class DictItemDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+ @ApiModelProperty(value = "字典ID")
+ @NotBlank(message = "字典ID不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String dictId;
+ @ApiModelProperty(value = "字典项文本")
+ @NotBlank(message = "字典项文本不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String itemText;
+ @ApiModelProperty(value = "字典项值")
+ @NotBlank(message = "字典项值不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String itemValue;
+ @ApiModelProperty(value = "排序")
+ @NotNull(message = "排序不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private Integer itemSort;
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictSmallDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictSmallDto.java
new file mode 100644
index 0000000..efe69b5
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/DictSmallDto.java
@@ -0,0 +1,18 @@
+
+package cn.datax.service.system.api.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27
+*/
+@Getter
+@Setter
+public class DictSmallDto implements Serializable {
+
+ private Long id;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JobSmallDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JobSmallDto.java
new file mode 100644
index 0000000..5234b12
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JobSmallDto.java
@@ -0,0 +1,20 @@
+
+package cn.datax.service.system.api.dto;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+* @author AllDataDC
+* @date 2023-01-27 16:32:18
+*/
+@Data
+@NoArgsConstructor
+public class JobSmallDto implements Serializable {
+
+ private Long id;
+
+ private String name;
+}
\ No newline at end of file
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JwtUserDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JwtUserDto.java
new file mode 100644
index 0000000..0e5fef3
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/JwtUserDto.java
@@ -0,0 +1,66 @@
+
+package cn.datax.service.system.api.dto;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Getter
+@AllArgsConstructor
+public class JwtUserDto implements UserDetails {
+
+ public final UserLoginDto user;
+
+ private final List dataScopes;
+
+ private final List authorities;
+
+ public Set getRoles() {
+ return authorities.stream().map(AuthorityDto::getAuthority).collect(Collectors.toSet());
+ }
+
+ @Override
+ @JSONField(serialize = false)
+ public String getPassword() {
+ return user.getPassword();
+ }
+
+ @Override
+ @JSONField(serialize = false)
+ public String getUsername() {
+ return user.getUsername();
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @JSONField(serialize = false)
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ @JSONField(serialize = false)
+ public boolean isEnabled() {
+ return user.getEnabled();
+ }
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LogDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LogDto.java
new file mode 100644
index 0000000..0f86f9e
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LogDto.java
@@ -0,0 +1,61 @@
+package cn.datax.service.system.api.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+@ApiModel(value = "日志Model")
+@Data
+@Accessors(chain = true)
+public class LogDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ private String id;
+
+ @ApiModelProperty(value = "所属模块")
+ private String module;
+
+ @ApiModelProperty(value = "日志标题")
+ private String title;
+
+ @ApiModelProperty(value = "用户ID")
+ private String userId;
+
+ @ApiModelProperty(value = "用户名称")
+ private String userName;
+
+ @ApiModelProperty(value = "请求IP")
+ private String remoteAddr;
+
+ @ApiModelProperty(value = "请求URI")
+ private String requestUri;
+
+ @ApiModelProperty(value = "方法类名")
+ private String className;
+
+ @ApiModelProperty(value = "方法名称")
+ private String methodName;
+
+ @ApiModelProperty(value = "请求参数")
+ private String params;
+
+ @ApiModelProperty(value = "请求耗时")
+ private String time;
+
+ @ApiModelProperty(value = "浏览器名称")
+ private String browser;
+
+ @ApiModelProperty(value = "操作系统")
+ private String os;
+
+ @ApiModelProperty(value = "错误类型")
+ private String exCode;
+
+ @ApiModelProperty(value = "错误信息")
+ private String exMsg;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LoginLogDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LoginLogDto.java
new file mode 100644
index 0000000..901335a
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/LoginLogDto.java
@@ -0,0 +1,38 @@
+package cn.datax.service.system.api.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 登录日志信息表 实体DTO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-29
+ */
+@ApiModel(value = "登录日志信息表Model")
+@Data
+public class LoginLogDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ private String id;
+ @ApiModelProperty(value = "操作系统")
+ private String opOs;
+ @ApiModelProperty(value = "浏览器类型")
+ private String opBrowser;
+ @ApiModelProperty(value = "登录IP地址")
+ private String opIp;
+ @ApiModelProperty(value = "登录时间")
+ private LocalDateTime opDate;
+ @ApiModelProperty(value = "登录用户ID")
+ private String userId;
+ @ApiModelProperty(value = "登录用户名称")
+ private String userName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/MenuDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/MenuDto.java
new file mode 100644
index 0000000..3f4866d
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/MenuDto.java
@@ -0,0 +1,66 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@ApiModel(value = "资源Model")
+@Data
+public class MenuDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+
+ @ApiModelProperty(value = "父资源ID")
+ @NotBlank(message = "父资源ID不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String parentId;
+
+ @ApiModelProperty(value = "资源名称")
+ @NotBlank(message = "资源名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String menuName;
+
+ @ApiModelProperty(value = "对应路由地址path")
+ private String menuPath;
+
+ @ApiModelProperty(value = "对应路由组件component")
+ private String menuComponent;
+
+ @ApiModelProperty(value = "对应路由默认跳转地址redirect")
+ private String menuRedirect;
+
+ @ApiModelProperty(value = "权限标识")
+ private String menuPerms;
+
+ @ApiModelProperty(value = "资源图标")
+ private String menuIcon;
+
+ @ApiModelProperty(value = "资源类型")
+ @NotNull(message = "类型不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String menuType;
+
+ @ApiModelProperty(value = "资源编码")
+ private String menuCode;
+
+ @ApiModelProperty(value = "隐藏")
+ @NotNull(message = "隐藏不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String menuHidden;
+
+ @ApiModelProperty(value = "排序")
+ @NotNull(message = "排序不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private Integer menuSort;
+
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/PostDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/PostDto.java
new file mode 100644
index 0000000..16e9351
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/PostDto.java
@@ -0,0 +1,32 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@ApiModel(value = "岗位Model")
+@Data
+public class PostDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+
+ @ApiModelProperty(value = "岗位名称")
+ @NotBlank(message = "岗位名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String postName;
+
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleDto.java
new file mode 100644
index 0000000..d22398d
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleDto.java
@@ -0,0 +1,49 @@
+package cn.datax.service.system.api.dto;
+
+import cn.datax.common.validate.ValidationGroups;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+@ApiModel(value = "角色Model")
+@Data
+public class RoleDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ @NotBlank(message = "主键ID不能为空", groups = {ValidationGroups.Update.class})
+ private String id;
+
+ @ApiModelProperty(value = "角色名称")
+ @NotBlank(message = "角色名称不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String roleName;
+
+ @ApiModelProperty(value = "角色编码")
+ @NotBlank(message = "角色编码不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String roleCode;
+
+ @ApiModelProperty(value = "数据范围")
+ @NotNull(message = "数据范围不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String dataScope;
+
+ @ApiModelProperty(value = "资源")
+ @NotEmpty(message = "资源不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private List menuList;
+
+ @ApiModelProperty(value = "数据范围为2时自定义数据权限")
+ private List deptList;
+
+ @ApiModelProperty(value = "状态")
+ @NotNull(message = "状态不能为空", groups = {ValidationGroups.Insert.class, ValidationGroups.Update.class})
+ private String status;
+
+ @ApiModelProperty(value = "备注")
+ private String remark;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleSmallDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleSmallDto.java
new file mode 100644
index 0000000..7c324ac
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/RoleSmallDto.java
@@ -0,0 +1,22 @@
+
+package cn.datax.service.system.api.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Data
+public class RoleSmallDto implements Serializable {
+
+ private Long id;
+
+ private String name;
+
+ private Integer level;
+
+ private String dataScope;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserDto.java
new file mode 100644
index 0000000..13c86dc
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserDto.java
@@ -0,0 +1,54 @@
+
+package cn.datax.service.system.api.dto;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * @author AllDataDC
+ * @date 2023-01-27
+ */
+@Getter
+@Setter
+public class UserDto extends BaseDTO implements Serializable {
+
+ private Long id;
+
+ private Set roles;
+
+ private Set jobs;
+
+ private DeptSmallDto dept;
+
+ private Long deptId;
+
+ private String username;
+
+ private String nickName;
+
+ private String email;
+
+ private String phone;
+
+ private String gender;
+
+ private String avatarName;
+
+ private String avatarPath;
+
+ @JSONField(serialize = false)
+ private String password;
+
+ private Boolean enabled;
+
+ @JSONField(serialize = false)
+ private Boolean isAdmin = false;
+
+ private Date pwdResetTime;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserLoginDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserLoginDto.java
new file mode 100644
index 0000000..12e89e6
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserLoginDto.java
@@ -0,0 +1,14 @@
+
+package cn.datax.service.system.api.dto;
+
+/**
+ * @author AllDataDC
+ * @description 用户缓存时使用
+ * @date 2023-01-27
+ **/
+public class UserLoginDto extends UserDto {
+
+ private String password;
+
+ public Boolean isAdmin;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserPasswordDto.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserPasswordDto.java
new file mode 100644
index 0000000..6b92b2c
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/dto/UserPasswordDto.java
@@ -0,0 +1,26 @@
+package cn.datax.service.system.api.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+
+@ApiModel(value = "用户密码Model")
+@Data
+public class UserPasswordDto implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ @ApiModelProperty(value = "主键ID")
+ private String id;
+
+ @ApiModelProperty(value = "密码")
+ @NotBlank(message = "密码不能为空")
+ private String password;
+
+ @ApiModelProperty(value = "旧密码")
+ @NotBlank(message = "旧密码不能为空")
+ private String oldPassword;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/ConfigEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/ConfigEntity.java
new file mode 100644
index 0000000..d43aafe
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/ConfigEntity.java
@@ -0,0 +1,41 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import cn.datax.common.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ * 系统参数配置信息表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_config")
+public class ConfigEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 参数名称
+ */
+ private String configName;
+
+ /**
+ * 参数键名
+ */
+ private String configKey;
+
+ /**
+ * 参数键值
+ */
+ private String configValue;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptEntity.java
new file mode 100644
index 0000000..0465ca7
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import cn.datax.common.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_dept")
+public class DeptEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 父部门ID
+ */
+ private String parentId;
+
+ /**
+ * 部门名称
+ */
+ private String deptName;
+
+ /**
+ * 一级:10
+ * 二级:10 001、10 002
+ * 三级:10 001 001、10 001 002、10 002 001
+ * 部门编码(数据权限优化查询速度 like '10001%')
+ */
+ private String deptNo;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptRelationEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptRelationEntity.java
new file mode 100644
index 0000000..0784b4f
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DeptRelationEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 部门关系表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-22
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_dept_relation")
+public class DeptRelationEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 祖先节点
+ */
+ private String ancestor;
+
+ /**
+ * 后代节点
+ */
+ private String descendant;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictEntity.java
new file mode 100644
index 0000000..228cd73
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import cn.datax.common.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ *
+ * 字典编码信息表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_dict")
+public class DictEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 字典名称
+ */
+ private String dictName;
+
+ /**
+ * 字典编码
+ */
+ private String dictCode;
+
+ @TableField(exist = false)
+ private List items;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictItemEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictItemEntity.java
new file mode 100644
index 0000000..e1b8416
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/DictItemEntity.java
@@ -0,0 +1,46 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import cn.datax.common.base.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ * 字典项信息表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_dict_item")
+public class DictItemEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 字典id
+ */
+ private String dictId;
+
+ /**
+ * 字典项文本
+ */
+ private String itemText;
+
+ /**
+ * 字典项值
+ */
+ private String itemValue;
+
+ /**
+ * 排序
+ */
+ private Integer itemSort;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LogEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LogEntity.java
new file mode 100644
index 0000000..f70d11f
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LogEntity.java
@@ -0,0 +1,108 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_log")
+public class LogEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 所属模块
+ */
+ private String module;
+
+ /**
+ * 日志标题
+ */
+ private String title;
+
+ /**
+ * 用户ID
+ */
+ private String userId;
+
+ /**
+ * 用户名称
+ */
+ private String userName;
+
+ /**
+ * 请求IP
+ */
+ private String remoteAddr;
+
+ /**
+ * 请求URI
+ */
+ private String requestUri;
+
+ /**
+ * 方法类名
+ */
+ private String className;
+
+ /**
+ * 方法名称
+ */
+ private String methodName;
+
+ /**
+ * 请求参数
+ */
+ private String params;
+
+ /**
+ * 请求耗时
+ */
+ private String time;
+
+ /**
+ * 浏览器名称
+ */
+ private String browser;
+
+ /**
+ * 操作系统
+ */
+ private String os;
+
+ /**
+ * 错误类型
+ */
+ private String exCode;
+
+ /**
+ * 错误信息
+ */
+ private String exMsg;
+
+ /**
+ * 创建时间
+ */
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ @TableField(value = "create_time", fill = FieldFill.INSERT)
+ private LocalDateTime createTime;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LoginLogEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LoginLogEntity.java
new file mode 100644
index 0000000..5090b12
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/LoginLogEntity.java
@@ -0,0 +1,62 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ * 登录日志信息表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-29
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_login_log")
+public class LoginLogEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 操作系统
+ */
+ private String opOs;
+
+ /**
+ * 浏览器类型
+ */
+ private String opBrowser;
+
+ /**
+ * 登录IP地址
+ */
+ private String opIp;
+
+ /**
+ * 登录时间
+ */
+ private LocalDateTime opDate;
+
+ /**
+ * 登录用户ID
+ */
+ private String userId;
+
+ /**
+ * 登录用户名称
+ */
+ private String userName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/MenuEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/MenuEntity.java
new file mode 100644
index 0000000..84068fc
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/MenuEntity.java
@@ -0,0 +1,80 @@
+package cn.datax.service.system.api.entity;
+
+import cn.datax.common.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_menu")
+public class MenuEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 父资源ID
+ */
+ private String parentId;
+
+ /**
+ * 资源名称
+ */
+ private String menuName;
+
+ /**
+ * 对应路由地址path
+ */
+ private String menuPath;
+
+ /**
+ * 对应路由组件component
+ */
+ private String menuComponent;
+
+ /**
+ * 对应路由默认跳转地址redirect
+ */
+ private String menuRedirect;
+
+ /**
+ * 权限标识
+ */
+ private String menuPerms;
+
+ /**
+ * 资源图标
+ */
+ private String menuIcon;
+
+ /**
+ * 资源类型(0模块,1菜单,2按钮)
+ */
+ private String menuType;
+
+ /**
+ * 资源编码
+ */
+ private String menuCode;
+
+ /**
+ * 资源隐藏(0否,1是)
+ */
+ private String menuHidden;
+
+ /**
+ * 排序
+ */
+ private Integer menuSort;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/PostEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/PostEntity.java
new file mode 100644
index 0000000..5d88d35
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/PostEntity.java
@@ -0,0 +1,30 @@
+package cn.datax.service.system.api.entity;
+
+import cn.datax.common.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName("sys_market_post")
+public class PostEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 岗位名称
+ */
+ private String postName;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleDeptEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleDeptEntity.java
new file mode 100644
index 0000000..f7d065a
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleDeptEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ * 角色和部门关联表
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-22
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_role_dept")
+public class RoleDeptEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 角色ID
+ */
+ private String roleId;
+
+ /**
+ * 部门ID
+ */
+ private String deptId;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleEntity.java
new file mode 100644
index 0000000..0181aa9
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleEntity.java
@@ -0,0 +1,49 @@
+package cn.datax.service.system.api.entity;
+
+import cn.datax.common.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName(value = "sys_market_role", autoResultMap = true)
+public class RoleEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 角色名称
+ */
+ private String roleName;
+
+ /**
+ * 角色编码
+ */
+ private String roleCode;
+
+ /**
+ * 数据范围(1:全部数据权限 2:自定义数据权限 3:本部门数据权限 4:本部门及以下数据权限 5:仅本人数据权限)
+ */
+ private String dataScope;
+
+ @TableField(exist = false)
+ private List menus;
+
+ @TableField(exist = false)
+ private List depts;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleMenuEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleMenuEntity.java
new file mode 100644
index 0000000..daac42f
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/RoleMenuEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_role_menu")
+public class RoleMenuEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 角色ID
+ */
+ private String roleId;
+
+ /**
+ * 菜单ID
+ */
+ private String menuId;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserEntity.java
new file mode 100644
index 0000000..4d6b0ce
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserEntity.java
@@ -0,0 +1,72 @@
+package cn.datax.service.system.api.entity;
+
+import cn.datax.common.base.BaseEntity;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@TableName(value = "sys_market_user", autoResultMap = true)
+public class UserEntity extends BaseEntity {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 用户名称
+ */
+ private String username;
+
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+
+ /**
+ * 用户密码
+ */
+ private String password;
+
+ /**
+ * 电子邮箱
+ */
+ private String email;
+
+ /**
+ * 手机号码
+ */
+ private String phone;
+
+ /**
+ * 出生日期
+ */
+ private LocalDate birthday;
+
+ /**
+ * 部门
+ */
+ private String deptId;
+
+ @TableField(exist = false)
+ private DeptEntity dept;
+
+ @TableField(exist = false)
+ private List roles;
+
+ @TableField(exist = false)
+ private List posts;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserPostEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserPostEntity.java
new file mode 100644
index 0000000..be39e7b
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserPostEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_user_post")
+public class UserPostEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 用户ID
+ */
+ private String userId;
+
+ /**
+ * 岗位ID
+ */
+ private String postId;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserRoleEntity.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserRoleEntity.java
new file mode 100644
index 0000000..4696349
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/entity/UserRoleEntity.java
@@ -0,0 +1,42 @@
+package cn.datax.service.system.api.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-11
+ */
+@Data
+@Accessors(chain = true)
+@TableName("sys_market_user_role")
+public class UserRoleEntity implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private String id;
+
+ /**
+ * 用户ID
+ */
+ private String userId;
+
+ /**
+ * 角色ID
+ */
+ private String roleId;
+
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/UserServiceFeign.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/UserServiceFeign.java
new file mode 100644
index 0000000..6b1c716
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/UserServiceFeign.java
@@ -0,0 +1,14 @@
+package cn.datax.service.system.api.feign;
+
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.datax.service.system.api.feign.factory.UserServiceFeignFallbackFactory;
+import cn.datax.service.system.api.vo.UserInfo;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+
+@FeignClient(contextId = "userServiceFeign", value = "service-system", fallbackFactory = UserServiceFeignFallbackFactory.class)
+public interface UserServiceFeign {
+ @GetMapping("/api/users/{username}")
+ JwtUserDto loginByUsername(@PathVariable("username") String username);
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/factory/UserServiceFeignFallbackFactory.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/factory/UserServiceFeignFallbackFactory.java
new file mode 100644
index 0000000..9e74862
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/factory/UserServiceFeignFallbackFactory.java
@@ -0,0 +1,17 @@
+package cn.datax.service.system.api.feign.factory;
+
+import cn.datax.service.system.api.feign.UserServiceFeign;
+import cn.datax.service.system.api.feign.fallback.UserServiceFeignFallbackImpl;
+import feign.hystrix.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class UserServiceFeignFallbackFactory implements FallbackFactory {
+
+ @Override
+ public UserServiceFeign create(Throwable throwable) {
+ UserServiceFeignFallbackImpl userServiceFeignFallback = new UserServiceFeignFallbackImpl();
+ userServiceFeignFallback.setCause(throwable);
+ return userServiceFeignFallback;
+ }
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/fallback/UserServiceFeignFallbackImpl.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/fallback/UserServiceFeignFallbackImpl.java
new file mode 100644
index 0000000..47384de
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/feign/fallback/UserServiceFeignFallbackImpl.java
@@ -0,0 +1,21 @@
+package cn.datax.service.system.api.feign.fallback;
+
+import cn.datax.service.system.api.dto.JwtUserDto;
+import cn.datax.service.system.api.feign.UserServiceFeign;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class UserServiceFeignFallbackImpl implements UserServiceFeign {
+
+ @Setter
+ private Throwable cause;
+
+ @Override
+ public JwtUserDto loginByUsername(String username) {
+ log.error("feign 调用{}出错", username, cause);
+ return null;
+ }
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/ConfigQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/ConfigQuery.java
new file mode 100644
index 0000000..ea360e3
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/ConfigQuery.java
@@ -0,0 +1,20 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *
+ * 系统参数配置信息表 查询实体
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-19
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class ConfigQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictItemQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictItemQuery.java
new file mode 100644
index 0000000..f08796a
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictItemQuery.java
@@ -0,0 +1,27 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *
+ * 字典项信息表 查询实体
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DictItemQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 字典id
+ */
+ private String dictId;
+ private String itemText;
+ private String itemValue;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictQuery.java
new file mode 100644
index 0000000..20a9172
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/DictQuery.java
@@ -0,0 +1,23 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *
+ * 字典编码信息表 查询实体
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class DictQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ private String dictName;
+ private String dictCode;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LogQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LogQuery.java
new file mode 100644
index 0000000..259d8c6
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LogQuery.java
@@ -0,0 +1,27 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LogQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 日志标题
+ */
+ private String title;
+
+ /**
+ * 所属模块
+ */
+ private String module;
+
+ /**
+ * 用户名称
+ */
+ private String userName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LoginLogQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LoginLogQuery.java
new file mode 100644
index 0000000..6c28b65
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/LoginLogQuery.java
@@ -0,0 +1,25 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ *
+ * 登录日志信息表 查询实体
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LoginLogQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 登录用户名称
+ */
+ private String userName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/PostQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/PostQuery.java
new file mode 100644
index 0000000..a873417
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/PostQuery.java
@@ -0,0 +1,17 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class PostQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 岗位名称
+ */
+ private String postName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/RoleQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/RoleQuery.java
new file mode 100644
index 0000000..4045bed
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/RoleQuery.java
@@ -0,0 +1,17 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class RoleQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 角色名称
+ */
+ private String roleName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/UserQuery.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/UserQuery.java
new file mode 100644
index 0000000..8111f2c
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/query/UserQuery.java
@@ -0,0 +1,25 @@
+package cn.datax.service.system.api.query;
+
+import cn.datax.common.base.BaseQueryParams;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class UserQuery extends BaseQueryParams {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 用户名称
+ */
+ private String username;
+ /**
+ * 用户昵称
+ */
+ private String nickname;
+ /**
+ * 部门ID
+ */
+ private String deptId;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/ConfigVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/ConfigVo.java
new file mode 100644
index 0000000..802a796
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/ConfigVo.java
@@ -0,0 +1,30 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 系统参数配置信息表 实体VO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-19
+ */
+@Data
+public class ConfigVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String configName;
+ private String configKey;
+ private String configValue;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DeptVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DeptVo.java
new file mode 100644
index 0000000..81c2a4a
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DeptVo.java
@@ -0,0 +1,24 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class DeptVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String parentId;
+ private String deptName;
+ private String deptNo;
+ private List items;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictItemVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictItemVo.java
new file mode 100644
index 0000000..d0aefe7
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictItemVo.java
@@ -0,0 +1,31 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 字典项信息表 实体VO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+public class DictItemVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String dictId;
+ private String itemText;
+ private String itemValue;
+ private Integer itemSort;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictVo.java
new file mode 100644
index 0000000..337e770
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/DictVo.java
@@ -0,0 +1,29 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 字典编码信息表 实体VO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-17
+ */
+@Data
+public class DictVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String dictName;
+ private String dictCode;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LogVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LogVo.java
new file mode 100644
index 0000000..f3c78d7
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LogVo.java
@@ -0,0 +1,31 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public class LogVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String module;
+ private String title;
+ private String userId;
+ private String userName;
+ private String remoteAddr;
+ private String requestUri;
+ private String className;
+ private String methodName;
+ private String params;
+ private String time;
+ private String browser;
+ private String os;
+ private String exCode;
+ private String exMsg;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LoginLogVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LoginLogVo.java
new file mode 100644
index 0000000..284b371
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/LoginLogVo.java
@@ -0,0 +1,30 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 登录日志信息表 实体VO
+ *
+ *
+ * @author AllDataDC
+ * @since 2023-01-29
+ */
+@Data
+public class LoginLogVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String opOs;
+ private String opBrowser;
+ private String opIp;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime opDate;
+ private String userId;
+ private String userName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/MenuVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/MenuVo.java
new file mode 100644
index 0000000..20839fa
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/MenuVo.java
@@ -0,0 +1,30 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public class MenuVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String parentId;
+ private String menuName;
+ private String menuPath;
+ private String menuComponent;
+ private String menuRedirect;
+ private String menuPerms;
+ private String menuIcon;
+ private String menuType;
+ private String menuCode;
+ private String menuHidden;
+ private Integer menuSort;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/PostVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/PostVo.java
new file mode 100644
index 0000000..7c150ff
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/PostVo.java
@@ -0,0 +1,20 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+public class PostVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String postName;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/RoleVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/RoleVo.java
new file mode 100644
index 0000000..f30b7b6
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/RoleVo.java
@@ -0,0 +1,25 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class RoleVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String roleName;
+ private String roleCode;
+ private String dataScope;
+ private List menus;
+ private List depts;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserInfo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserInfo.java
new file mode 100644
index 0000000..c88421d
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserInfo.java
@@ -0,0 +1,22 @@
+package cn.datax.service.system.api.vo;
+
+import cn.datax.service.system.api.dto.UserDto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class UserInfo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ * 用户基本信息
+ */
+ private UserDto userVo;
+ /**
+ * 权限标识集合
+ */
+ private String[] perms;
+}
diff --git a/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserVo.java b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserVo.java
new file mode 100644
index 0000000..a3652e1
--- /dev/null
+++ b/studio/common/common-service-api/src/main/java/cn/datax/service/system/api/vo/UserVo.java
@@ -0,0 +1,32 @@
+package cn.datax.service.system.api.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Data
+public class UserVo implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ private String id;
+ private String status;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+ private LocalDateTime createTime;
+ private String remark;
+ private String username;
+ private String nickname;
+ private String password;
+ private String email;
+ private String phone;
+ @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+ private LocalDate birthday;
+ private String deptId;
+ private DeptVo dept;
+ private List