diff --git a/.gitignore b/.gitignore index 1ebd874..510f066 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .idea/ .settings/ .vscode/ +*.iml vendor/ composer.lock gitpush.sh @@ -16,15 +17,13 @@ cbuild main output/ manifest/output/ +manifest/config/config.yaml temp/ resource/log/ -#resource/public/ +resource/public/* logs/ upload/product_icon/ -manifest/config/config.yaml -manifest/docker-compose/emqx/ -manifest/docker-compose/mysql/data -manifest/docker-compose/redis/data -manifest/docker-compose/taos/ -manifest/docker-compose/iot-open/resource/log -extend/log/ \ No newline at end of file +extend/log/ +hack/config.yaml +go.sum +gf diff --git a/LICENSE b/LICENSE index 3dc1724..9c6a6df 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,165 @@ - GNU GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - SagooIoT Copyright (C) 2024 Sagoo Cloud Technology Co., Ltd . - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. \ No newline at end of file + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser 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 +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 378d7b0..0000000 --- a/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -ROOT_DIR = $(shell pwd) -NAMESPACE = "default" -DEPLOY_NAME = "focus-single" -DOCKER_NAME = "focus-single" - -# Install/Update to the latest CLI tool. -.PHONY: cli -cli: - @set -e; \ - wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \ - chmod +x gf && \ - ./gf install -y && \ - rm ./gf - - -# Check and install CLI tool. -.PHONY: cli.install -cli.install: - @set -e; \ - gf -v > /dev/null 2>&1 || if [[ $? -neq 0 ]]; then \ - echo "GoFame CLI is not installed, start proceeding auto installation..."; \ - make cli; \ - fi; - - -# Generate Go files for DAO/DO/Entity. -.PHONY: dao -dao: cli.install - @gf gen dao - - - -# Build image, deploy image and yaml to current kubectl environment and make port forward to local machine. -.PHONY: start -start: - @set -e; \ - make image; \ - make deploy; \ - make port; - -# Build docker image. -.PHONY: image -image: cli.install - $(eval _TAG = $(if ${TAG}, ${TAG}, latest)) - $(eval _PUSH = $(if ${PUSH}, ${PUSH}, )) - @gf docker -p -b "-a amd64 -s linux -p temp" -t $(DOCKER_NAME):${_TAG}; - - -# Build docker image and automatically push to docker repo. -.PHONY: image.push -image.push: - @make image PUSH=-p; - - -# Deploy image and yaml to current kubectl environment. -.PHONY: deploy -deploy: - $(eval _ENV = $(if ${ENV}, ${ENV}, develop-tke)) - - @set -e; \ - mkdir -p $(ROOT_DIR)/temp/kustomize;\ - cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\ - kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\ - kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \ - kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; - - diff --git a/README.zh-cn.md b/README.zh-cn.md index b91811f..41addec 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -1,4 +1,4 @@ -Sagoo IOT +SagooIOT Community V2 ========
@@ -53,6 +53,25 @@ SagooIOT是一个基于golang开发的轻量级的物联网平台。支持跨平 密码:admin123456 +**注意:** + +当前主分支为V2版本,V1版本请切换到V1分支 https://github.com/sagoo-cloud/sagooiot/tree/sagooiot-v1 + + +## V2版变化 + +1,重构设备数据上报处理链路,增加中间缓存队列,提高数据上报处理效率。 +2,重构缓存处理,统一使用方式。并对多处频繁调用的数据进行了缓存处理,提高数据处理效率。 +3,重构消息队列及定时任务的处理,改为分布式的任务队列处理方式,提高消息队列的处理效率及可靠性,并提供可视化的消息队列监控界面。 +4,重构部分代码的编写方式,规范入参及接口处理方式,提高代码可读性及可维护性。产品与设备,所涉及调用统一为key的方式。 +5,插件的编写方式进行了调整,独立出来,方便插件的编写及维护,并简化主工程的代码量。 +6,增加模块化的开发方式,进行模块功能与核心功能分离,方便功能的扩展及维护,并简化主工程的代码量。 +7,调整目录结构,公共处理统一到pkg目录中,方便其它功能开发调用及代码的维护管理。 +8,增加核心处理程序、web服务程序、任务队列处理程序分离单独运行的支持,提高程序的稳定性及可靠性。 +9,强化性能分析及监控功能,方便对系统进行性能分析及监控。并提供可视化的性能分析及监控界面。 + + + ## 平台简介 * 基于全新Go Frame 2.0+Vue3+Element Plus开发的全栈前后端分离的管理系统 * 前端采用vue-next-admin 、Vue、Element UI。 @@ -107,3 +126,10 @@ SagooIOT是一个基于golang开发的轻量级的物联网平台。支持跨平 ## 免责声明: SagooIOT社区版是一个开源学习项目,与商业行为无关。用户在使用该项目时,应遵循法律法规,不得进行非法活动。如果SagooIOT发现用户有违法行为,将会配合相关机关进行调查并向政府部门举报。用户因非法行为造成的任何法律责任均由用户自行承担,如因用户使用造成第三方损害的,用户应当依法予以赔偿。使用SagooIOT所有相关资源均由用户自行承担风险. + + + + + + + diff --git a/api/v1/alarm/alarm_level.go b/api/v1/alarm/alarm_level.go index bcc64dd..6bada67 100644 --- a/api/v1/alarm/alarm_level.go +++ b/api/v1/alarm/alarm_level.go @@ -1,7 +1,7 @@ package alarm import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/alarm/alarm_log.go b/api/v1/alarm/alarm_log.go index e9893a2..3a75f16 100644 --- a/api/v1/alarm/alarm_log.go +++ b/api/v1/alarm/alarm_log.go @@ -1,7 +1,8 @@ package alarm import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) @@ -16,14 +17,17 @@ type AlarmLogDetailRes struct { type AlarmLogListReq struct { g.Meta `path:"/log/list" method:"get" summary:"告警日志" tags:"告警"` - *model.AlarmLogListInput + Status string `p:"status"` //告警状态 + common.PaginationReq } type AlarmLogListRes struct { *model.AlarmLogListOutput } type AlarmLogHandleReq struct { - g.Meta `path:"/log/handle" method:"post" summary:"告警处理" tags:"告警"` - model.AlarmLogHandleInput + g.Meta `path:"/log/handle" method:"post" summary:"告警处理" tags:"告警"` + Id uint64 `json:"id" dc:"告警日志ID" v:"required#告警日志ID不能为空"` + Status int `json:"status" d:"1" dc:"处理状态" v:"required|in:1,2#请选择处理状态|未知的处理状态,请正确选择"` + Content string `json:"content" dc:"处理意见"` } type AlarmLogHandleRes struct{} diff --git a/api/v1/alarm/alarm_rule.go b/api/v1/alarm/alarm_rule.go index c87601d..1614c27 100644 --- a/api/v1/alarm/alarm_rule.go +++ b/api/v1/alarm/alarm_rule.go @@ -1,14 +1,15 @@ package alarm import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) type AlarmRuleListReq struct { g.Meta `path:"/rule/list" method:"get" summary:"告警规则列表" tags:"告警"` - *model.AlarmRuleListInput + common.PaginationReq } type AlarmRuleListRes struct { *model.AlarmRuleListOutput @@ -71,7 +72,20 @@ type AlarmRuleTriggerParamReq struct { g.Meta `path:"/rule/trigger_param" method:"get" summary:"触发条件参数" tags:"告警"` ProductKey string `json:"productKey" dc:"产品标识"` TriggerType int `json:"triggerType" dc:"触发类型:1=上线,2=离线,3=属性上报,4=事件上报"` + EventKey string `json:"eventKey" dc:"事件标识"` } type AlarmRuleTriggerParamRes struct { List []model.TriggerParamOutput `json:"list" dc:"触发条件参数列表"` } + +type AlarmCronRuleAddReq struct { + g.Meta `path:"/rule/cron/add" method:"post" summary:"新增定时触发告警规则" tags:"告警"` + *model.AlarmCronRuleAddInput +} +type AlarmCronRuleAddRes struct{} + +type AlarmCronRuleEditReq struct { + g.Meta `path:"/rule/cron/edit" method:"put" summary:"编辑定时触发告警规则" tags:"告警"` + *model.AlarmCronRuleEditInput +} +type AlarmCronRuleEditRes struct{} diff --git a/api/v1/analysis/alarm.go b/api/v1/analysis/alarm.go new file mode 100644 index 0000000..48a4a18 --- /dev/null +++ b/api/v1/analysis/alarm.go @@ -0,0 +1,53 @@ +package analysis + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" +) + +// DeviceAlertCountByYearMonthReq 按年度每月设备告警数统计 +type DeviceAlertCountByYearMonthReq struct { + g.Meta `path:"/deviceAlertCountByYearMonth" method:"get" summary:"按年度每月设备告警数统计" tags:"IOT数据分析"` + Year string `json:"year" v:"required#日期不能为空" dc:"日期:年-yyyy"` +} +type DeviceAlertCountByYearMonthRes struct { + Data []model.CountData `json:"data" dc:"月设备告警计数"` +} + +// DeviceAlertCountByMonthDayReq 按月度每日设备告警数统计 +type DeviceAlertCountByMonthDayReq struct { + g.Meta `path:"/deviceAlertCountByMonthDay" method:"get" summary:"按月度每日设备告警数统计" tags:"IOT数据分析"` + Month string `json:"month" v:"required#日期不能为空" dc:"日期:年-月 yyyy-MM"` +} +type DeviceAlertCountByMonthDayRes struct { + Data []model.CountData `json:"data" dc:"日设备告警计数"` +} + +// DeviceAlertCountByDayHourReq 按日每小时设备告警数统计 +type DeviceAlertCountByDayHourReq struct { + g.Meta `path:"/deviceAlertCountsByDayHour" method:"get" summary:"按日每小时设备告警数统计" tags:"IOT数据分析"` + Day string `json:"day" v:"required#日期不能为空" dc:"日期:年-月-日 yyyy-MM-dd"` +} +type DeviceAlertCountByDayHourRes struct { + Data []model.CountData `json:"data" dc:"小时设备告警计数"` +} + +// DeviceAlarmTotalCountReq 指定时间的告警总数统计,按年,月,日分别指定 +type DeviceAlarmTotalCountReq struct { + g.Meta `path:"/deviceAlarmTotalCount" method:"get" summary:"告警总数统计(当年、当月、当日)" tags:"IOT数据分析"` + DateType string `json:"dateType" v:"required#日期类型不能为空" dc:"日期类型:year 年,month 月,day 日"` + Date string `json:"date" v:"required#日期不能为空" dc:"日期:年 yyyy,月 yyyy-MM,日 yyyy-MM-dd"` +} +type DeviceAlarmTotalCountRes struct { + Number int64 `json:"number" dc:"告警总数"` +} + +// DeviceAlarmLevelCountReq 告警级别统计 +type DeviceAlarmLevelCountReq struct { + g.Meta `path:"/deviceAlarmLevelCount" method:"get" summary:"告警按级别统计" tags:"IOT数据分析"` + DateType string `json:"dateType" v:"required#日期类型不能为空" dc:"日期类型:year 年,month 月,day 日"` + Date string `json:"date" v:"required#日期不能为空" dc:"日期:年 yyyy,月 MM,日 dd"` +} +type DeviceAlarmLevelCountRes struct { + Data []model.CountData `json:"data" dc:"告警级别统计"` +} diff --git a/api/v1/analysis/device.go b/api/v1/analysis/device.go new file mode 100644 index 0000000..c71abc0 --- /dev/null +++ b/api/v1/analysis/device.go @@ -0,0 +1,33 @@ +package analysis + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" +) + +// DeviceDataTotalCountReq 设备消息总数统计(当年、当月、当日) +type DeviceDataTotalCountReq struct { + g.Meta `path:"/deviceDataTotalCount" method:"get" summary:"设备消息总数统计(当年、当月、当日)" tags:"IOT数据分析"` + DateType string `json:"dateType" v:"required#日期类型不能为空" dc:"日期类型:year 年,month 月,day 日"` +} +type DeviceDataTotalCountRes struct { + Number int64 `json:"number" dc:"消息总数"` +} + +// DeviceOnlineOfflineCountReq 设备在线离线统计 +type DeviceOnlineOfflineCountReq struct { + g.Meta `path:"/deviceOnlineOfflineCount" method:"get" summary:"设备在线离线统计" tags:"IOT数据分析"` +} +type DeviceOnlineOfflineCountRes struct { + Data model.DeviceOnlineOfflineCount +} + +// DeviceDataCountReq 按年度每月设备消息统计 +type DeviceDataCountReq struct { + g.Meta `path:"/deviceDataCount" method:"get" summary:"按年度统计1-12月份设备消息统计" tags:"IOT数据分析"` + DateType string `json:"dateType" v:"required#日期类型不能为空" dc:"日期类型:year 年,month 月,day 日"` + //Date string `json:"date" v:"" dc:"日期:年-yyyy"` +} +type DeviceDataCountRes struct { + Data []model.CountData `json:"data" dc:"1-12月份设备消息量统计数据"` +} diff --git a/api/v1/analysis/deviceData.go b/api/v1/analysis/deviceData.go new file mode 100644 index 0000000..5d78a3a --- /dev/null +++ b/api/v1/analysis/deviceData.go @@ -0,0 +1,47 @@ +package analysis + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" + "sagooiot/internal/model" +) + +// DeviceDataReq 按年度每月设备消息统计 +type DeviceDataReq struct { + g.Meta `path:"/deviceData" method:"get" summary:"设备最近的数据" tags:"IOT数据分析"` + DeviceKey string `json:"deviceKey" v:"required#设备key不能为空" dc:"设备key"` + common.PaginationReq +} +type DeviceDataRes struct { + Data []interface{} + common.PaginationRes +} + +// DeviceDataForProductByLatestReq 按产品查询设备最近的数据 +type DeviceDataForProductByLatestReq struct { + g.Meta `path:"/deviceDataForProductByLatest" method:"get" summary:"按产品查询设备最近的数据" tags:"IOT数据分析"` + ProductKey string `json:"productKey" v:"required#产品key不能为空" dc:"产品key"` +} + +type DeviceDataForProductByLatestRes struct { + Data []model.DeviceDataRes +} + +type DeviceDataForTsdReq struct { + g.Meta `path:"/deviceDataForTsd" method:"get" summary:"设备的时序数据" tags:"IOT数据分析"` + common.PaginationReq +} +type DeviceDataForTsdRes struct { + Data []interface{} + common.PaginationRes +} + +// DeviceAlarmLogDataReq 设备告警日志数据请求 +type DeviceAlarmLogDataReq struct { + g.Meta `path:"/deviceAlarmLogData" method:"get" summary:"设备告警日志数据" tags:"IOT数据分析"` + common.PaginationReq +} +type DeviceAlarmLogDataRes struct { + Data interface{} + common.PaginationRes +} diff --git a/api/v1/analysis/product.go b/api/v1/analysis/product.go new file mode 100644 index 0000000..3a0ebd6 --- /dev/null +++ b/api/v1/analysis/product.go @@ -0,0 +1,25 @@ +package analysis + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// ProductCountReq 按年度每月设备消息统计 +type ProductCountReq struct { + g.Meta `path:"/productCount" method:"get" summary:"产品数量统计" tags:"IOT数据分析"` +} +type ProductCountRes struct { + Total int `json:"total" dc:"产品总数"` + Enable int `json:"enable" dc:"启用产品数"` + Disable int `json:"disable" dc:"禁用产品数"` + Added int `json:"added" dc:"新增产品数"` +} + +// DeviceCountForProductReq 获取属于该产品下的设备数量 +type DeviceCountForProductReq struct { + g.Meta `path:"/deviceCountForProduct" method:"get" summary:"获取属于该产品下的设备数量" tags:"IOT数据分析"` + ProductKey string `json:"productKey" v:"required#ProductKey,产品key不能为空" dc:"产品key"` +} +type DeviceCountForProductRes struct { + Number int `json:"number" dc:"设备数"` +} diff --git a/api/v1/common/api.go b/api/v1/common/api.go index 63fc5a7..1d59b9f 100644 --- a/api/v1/common/api.go +++ b/api/v1/common/api.go @@ -1,11 +1,15 @@ package common type PaginationReq struct { - KeyWord string `json:"keyWord" dc:"搜索关键字"` //搜索关键字 - DateRange []string `p:"dateRange"` //日期范围 - OrderBy string //排序方式 - PageNum int `json:"pageNum" in:"query" d:"1" v:"min:0#分页号码错误" dc:"分页号码,默认1"` - PageSize int `json:"PageSize" in:"query" d:"10" v:"max:50#分页数量最大50条" dc:"分页数量,最大50"` + Param map[string]interface{} `json:"param" dc:"搜索字段参数,写法param[字段名称]"` + KeyWord string `json:"keyWord" dc:"搜索关键字"` + Year string `json:"year" dc:"年份,如:2024"` + DateRange []string `json:"dateRange" dc:"日期范围,数组"` + Accurate string `json:"accurate" dc:"数据精确类型(m:月,d:天,h:小时)"` + AccurateRanges string `json:"accurateRanges" dc:"精确值,当accurate精确类型为d的时候,可以指定月份,当accurate为h的时候,可以指定某日的值"` + OrderBy string `json:"orderBy" dc:"排序方式"` //排序方式 + PageNum int `json:"pageNum" in:"query" d:"1" v:"min:0#分页号码错误" dc:"分页号码,默认1"` + PageSize int `json:"PageSize" in:"query" d:"10" v:"max:500#分页数量最大500条" dc:"分页数量,最大500"` } type PaginationRes struct { diff --git a/api/v1/common/base_db_link.go b/api/v1/common/base_db_link.go deleted file mode 100644 index 37df651..0000000 --- a/api/v1/common/base_db_link.go +++ /dev/null @@ -1,66 +0,0 @@ -package common - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type BaseDbLinkDoReq struct { - g.Meta `path:"/base/db/list" tags:"数据源管理" method:"get" summary:"数据源列表"` - Name string `p:"name" description:"数据源名称"` - Types string `p:"types" description:"驱动类型 mysql或oracle"` - Host string `p:"host" description:"主机地址"` - Port string `p:"port" description:"端口"` - UserName string `p:"user_name" description:"用户名称"` - Status int `p:"status" description:"状态:-1为全部,0为正常,1为停用"` - *PaginationReq -} -type BaseDbLinkDoRes struct { - Data []*model.BaseDbLinkRes - PaginationRes -} - -type AddBaseDbLinkReq struct { - g.Meta `path:"/base/db/add" tags:"数据源管理" method:"post" summary:"添加数据源"` - Name string `json:"name" description:"名称" v:"required#请输入数据源名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle" v:"required#请输入数据源驱动类型"` - Host string `json:"host" description:"主机地址" v:"required#请输入数据源主机地址"` - Port int `json:"port" description:"端口号" v:"required#请输入数据源端口号"` - UserName string `json:"userName" description:"用户名称" v:"required#请输入数据源用户名称"` - Password string `json:"password" description:"密码" v:"required#请输入数据源密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type AddBaseDbLinkRes struct { -} - -type DetailBaseDbLinkReq struct { - g.Meta `path:"/base/db/detail" tags:"数据源管理" method:"get" summary:"根据ID获取数据源详情"` - Id int `p:"id" description:"数据源ID" v:"required#ID不能为空"` -} -type DetailBaseDbLinkRes struct { - Data *model.DetailBaseDbLinkRes -} - -type EditBaseDbLinkReq struct { - g.Meta `path:"/base/db/edit" method:"put" summary:"编辑数据源" tags:"数据源管理"` - Id int `json:"id" description:"" v:"required#请输入数据源ID"` - Name string `json:"name" description:"名称" v:"required#请输入数据源名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle" v:"required#请输入数据源驱动类型"` - Host string `json:"host" description:"主机地址" v:"required#请输入数据源主机地址"` - Port int `json:"port" description:"端口号" v:"required#请输入数据源端口号"` - UserName string `json:"userName" description:"用户名称" v:"required#请输入数据源用户名称"` - Password string `json:"password" description:"密码" v:"required#请输入数据源密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type EditBaseDbLinkRes struct { -} - -type DelBaseDbLinkReq struct { - g.Meta `path:"/base/db/del" method:"delete" summary:"根据ID删除数据源" tags:"数据源管理"` - Id int `p:"id" description:"数据源ID" v:"required#ID不能为空"` -} -type DelBaseDbLinkRes struct { -} diff --git a/api/v1/common/check_auth.go b/api/v1/common/check_auth.go new file mode 100644 index 0000000..fafd5e0 --- /dev/null +++ b/api/v1/common/check_auth.go @@ -0,0 +1,20 @@ +package common + +import "github.com/gogf/gf/v2/frame/g" + +type IsTokenReq struct { + g.Meta `path:"/isToken" method:"get" summary:"验证token是否正确" tags:"公共方法"` +} +type IsTokenRes struct { + ExpiresAt int64 `json:"expiresAt"` + IsToken bool `json:"isToken"` + Auth string `json:"auth"` +} + +type CheckAccessAuthReq struct { + g.Meta `path:"/checkAccessAuth" method:"get" summary:"验证接口是否具有访问权限" tags:"公共方法"` + Address string `p:"address" description:"接口地址" v:"required#接口地址不能为空"` +} +type CheckAccessAuthRes struct { + IsAllow bool `json:"isAllow"` +} diff --git a/api/v1/common/city_data.go b/api/v1/common/city_data.go deleted file mode 100644 index 21906b4..0000000 --- a/api/v1/common/city_data.go +++ /dev/null @@ -1,45 +0,0 @@ -package common - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" -) - -type CityTreeReq struct { - g.Meta `path:"/city/tree" method:"get" summary:"获取城市列表" tags:"城市管理"` - Status int `json:"status" description:"状态:--1为全部,0为禁用,1为正常" ` - Name string `json:"name" description:"城市名" ` - Code string `json:"code" description:"城市编码" ` -} -type CityTreeRes struct { - Data []*model.CityTreeRes -} - -type AddCityReq struct { - g.Meta `path:"/city/add" method:"post" summary:"添加城市" tags:"城市管理"` - *model.AddCityReq -} -type AddCityRes struct { -} - -type EditCityReq struct { - g.Meta `path:"/city/edit" method:"put" summary:"编辑城市" tags:"城市管理"` - *model.EditCityReq -} -type EditCityRes struct { -} - -type GetCityByIdReq struct { - g.Meta `path:"/city/getInfoById" method:"get" summary:"根据ID获取城市信息" tags:"城市管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` -} -type GetCityByIdRes struct { - Data *model.CityRes -} - -type DelCityByIdReq struct { - g.Meta `path:"/city/del" method:"delete" summary:"根据ID删除城市信息" tags:"城市管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` -} -type DelCityByIdRes struct { -} diff --git a/api/v1/common/config_data.go b/api/v1/common/config_data.go index 905149c..2627b26 100644 --- a/api/v1/common/config_data.go +++ b/api/v1/common/config_data.go @@ -2,7 +2,7 @@ package common import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type ConfigSearchReq struct { @@ -35,7 +35,7 @@ type ConfigAddRes struct { type ConfigGetReq struct { g.Meta `path:"/config/get" tags:"系统参数管理" method:"get" summary:"获取系统参数"` - Id int `p:"id"` + Id int `p:"id" v:"required#ID不能为空"` } type ConfigGetRes struct { @@ -64,3 +64,38 @@ type ConfigDeleteReq struct { type ConfigDeleteRes struct { } + +type ConfigGetByKeyReq struct { + g.Meta `path:"/config/getInfoByKey" tags:"系统参数管理" method:"get" summary:"根据KEY获取系统参数"` + ConfigKey string `p:"configKey" description:"参数键名" v:"required#参数键名不能为空"` +} + +type ConfigGetByKeyRes struct { + g.Meta `mime:"application/json"` + Data *model.SysConfigRes `json:"data"` +} + +type ConfigGetByKeysReq struct { + g.Meta `path:"/config/getInfoByKeys" tags:"系统参数管理" method:"get" summary:"根据KEY数组获取系统参数"` + ConfigKey []string `p:"configKey" description:"参数键名" v:"required#参数键名不能为空"` +} + +type ConfigGetByKeysRes struct { + g.Meta `mime:"application/json"` + Data []*model.SysConfigRes `json:"data"` +} + +type GetSysConfigSettingReq struct { + g.Meta `path:"/getSysConfigSetting" method:"get" summary:"获取系统配置" tags:"系统参数管理"` + Types int `p:"types" description:"类型 0 基础配置 1 安全配置"` +} +type GetSysConfigSettingRes struct { + Info []*model.SysConfigRes `json:"data"` +} + +type EditSysConfigSettingReq struct { + g.Meta `path:"/editSysConfigSetting" method:"put" summary:"修改系统配置" tags:"系统参数管理"` + ConfigInfo []*model.EditConfigReq +} +type EditSysConfigSettingRes struct { +} diff --git a/api/v1/common/dict_data.go b/api/v1/common/dict_data.go index c67e4fb..d672371 100644 --- a/api/v1/common/dict_data.go +++ b/api/v1/common/dict_data.go @@ -2,7 +2,7 @@ package common import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) // GetDictReq 获取字典信息请求参数 diff --git a/api/v1/common/dict_type.go b/api/v1/common/dict_type.go index 334b456..8b20f08 100644 --- a/api/v1/common/dict_type.go +++ b/api/v1/common/dict_type.go @@ -2,7 +2,7 @@ package common import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type DictTypeSearchReq struct { @@ -10,7 +10,7 @@ type DictTypeSearchReq struct { DictName string `p:"dictName"` //字典名称 DictType string `p:"dictType"` //字典类型 Status string `p:"status"` //字典状态 - ModuleClassify string `p:"moduleClassify"` //字典模块分类 + ModuleClassify string `p:"moduleClassify"` //模块分类 *PaginationReq } @@ -25,7 +25,7 @@ type DictTypeAddReq struct { DictName string `p:"dictName" v:"required#字典名称不能为空"` DictType string `p:"dictType" v:"required#字典类型不能为空"` Status uint `p:"status" v:"required|in:0,1#状态不能为空|状态只能为0或1"` - ModuleClassify string `p:"moduleClassify"` + ModuleClassify string `p:"moduleClassify"` //模块分类 Remark string `p:"remark"` } @@ -48,7 +48,7 @@ type DictTypeEditReq struct { DictName string `p:"dictName" v:"required#字典名称不能为空"` DictType string `p:"dictType" v:"required#字典类型不能为空"` Status uint `p:"status" v:"required|in:0,1#状态不能为空|状态只能为0或1"` - ModuleClassify string `p:"moduleClassify"` + ModuleClassify string `p:"moduleClassify"` //模块分类 Remark string `p:"remark"` } diff --git a/api/v1/common/sysinfo.go b/api/v1/common/sysinfo.go index aa420ee..1b16289 100644 --- a/api/v1/common/sysinfo.go +++ b/api/v1/common/sysinfo.go @@ -1,17 +1,11 @@ package common -import "github.com/gogf/gf/v2/frame/g" +import ( + "github.com/gogf/gf/v2/frame/g" +) type GetSysInfoReq struct { g.Meta `path:"/sysinfo" tags:"公共方法" method:"get" summary:"获取系统相关的信息"` } type GetSysInfoRes g.Map - -type IsTokenReq struct { - g.Meta `path:"/isToken" method:"get" summary:"验证token是否正确" tags:"公共方法"` -} -type IsTokenRes struct { - ExpiresAt int64 `json:"expiresAt"` - IsToken bool `json:"isToken"` -} diff --git a/api/v1/common/upload.go b/api/v1/common/upload.go index 4d0f789..d4989cf 100644 --- a/api/v1/common/upload.go +++ b/api/v1/common/upload.go @@ -4,10 +4,12 @@ import "github.com/gogf/gf/v2/frame/g" type UploadSingleImgReq struct { g.Meta `path:"/singleImg" tags:"文件上传下载" method:"post" summary:"上传图片"` + Source int `json:"source" dc:"本地-0、腾讯云-1、阿里云-2、七牛云-3、MinIO-4"` } type UploadSingleFileReq struct { g.Meta `path:"/singleFile" tags:"文件上传下载" method:"post" summary:"上传文件"` + Source int `json:"source" dc:"本地-0、腾讯云-1、阿里云-2、七牛云-3、MinIO-4"` } type UploadSingleRes struct { @@ -17,10 +19,12 @@ type UploadSingleRes struct { type UploadMultipleImgReq struct { g.Meta `path:"/multipleImg" tags:"文件上传下载" method:"post" summary:"上传多图片"` + Source int `json:"source" dc:"本地-0、腾讯云-1、阿里云-2、七牛云-3、MinIO-4"` } type UploadMultipleFileReq struct { g.Meta `path:"/multipleFile" tags:"文件上传下载" method:"post" summary:"上传多文件"` + Source int `json:"source" dc:"本地-0、腾讯云-1、阿里云-2、七牛云-3、MinIO-4"` } type UploadMultipleRes []*UploadResponse diff --git a/api/v1/envirotronics/env_weather.go b/api/v1/envirotronics/env_weather.go deleted file mode 100644 index 13b2b4d..0000000 --- a/api/v1/envirotronics/env_weather.go +++ /dev/null @@ -1,46 +0,0 @@ -package envirotronics - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" -) - -type CityWeatherListReq struct { - g.Meta `path:"/weather/cityWeatherList" method:"get" summary:"获取城市的风力及日照时长" tags:"天气监测"` - Name string `json:"name" description:"名字"` -} -type CityWeatherListRes struct { - Info []*model.CityWeatherListRes -} - -type GetCityWeatherByIdReq struct { - g.Meta `path:"/weather/getInfoById" method:"get" summary:"根据ID获取指定城市的天气" tags:"天气监测"` - Id int `json:"id" description:"主键ID" v:"required#ID不能为空"` -} -type GetCityWeatherByIdRes struct { - Info *model.CityWeatherListRes -} - -type GetCityTemperatureByIdReq struct { - g.Meta `path:"/weather/getTemperatureEchartById" method:"get" summary:"根据ID获取指定城市的温度图表" tags:"天气监测"` - Id int `json:"id" description:"主键ID" v:"required#ID不能为空"` - Types int `json:"types" description:"类型 1 日 2周 3月 4年" v:"required#类型不能为空"` -} -type GetCityTemperatureByIdRes struct { - Info []*model.CityWeatherEchartRes - AvgInfo []*model.CityWeatherEchartRes - ForeCastInfo []*model.CityWeatherEchartRes - ForeCastAvgInfo []*model.CityWeatherEchartRes -} - -type GetCityWindpowerByIdReq struct { - g.Meta `path:"/weather/getWindpowerEchartById" method:"get" summary:"根据ID获取指定城市的风力图表" tags:"天气监测"` - Id int `json:"id" description:"主键ID" v:"required#ID不能为空"` - Types int `json:"types" description:"类型 1 日 2周 3月 4年" v:"required#类型不能为空"` -} -type GetCityWindpowerByIdRes struct { - Info []*model.CityWeatherEchartRes - AvgInfo []*model.CityWeatherEchartRes - ForeCastInfo []*model.CityWeatherEchartRes - ForeCastAvgInfo []*model.CityWeatherEchartRes -} diff --git a/api/v1/network/server.go b/api/v1/network/server.go index 7ce5d79..3d56a9e 100644 --- a/api/v1/network/server.go +++ b/api/v1/network/server.go @@ -1,9 +1,10 @@ package network import ( + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" ) // 这里是需要处理的地方,需要在这里调试好下面的几个接口 @@ -20,7 +21,7 @@ type GetNetworkServerListRes struct { // 获取指定ID的数据api type GetNetworkServerByIdReq struct { - g.Meta `path:"/get" method:"get" summary:"获取通讯服务列表" tags:"网络组件管理"` + g.Meta `path:"/get" method:"get" summary:"获取通讯服务详情" tags:"网络组件管理"` Id int `json:"id" description:"id" v:"required#id不能为空"` } type GetNetworkServerByIdRes struct { @@ -39,6 +40,14 @@ type AddNetworkServerReq struct { Devices string `json:"devices" description:"默认设备"` Remark string `json:"remark" description:"备注"` Status int `json:"status" description:""` + // 认证信息 + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick model.Stick `json:"stick" dc:"粘包处理方式"` } type AddNetworkServerRes struct{} @@ -55,6 +64,14 @@ type EditNetworkServerReq struct { Devices string `json:"devices" description:"默认设备"` Status int `json:"status" description:"状态"` Remark string `json:"remark" description:"备注"` + // 认证信息 + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick model.Stick `json:"stick" dc:"粘包处理方式"` } type EditNetworkServerRes struct{} diff --git a/api/v1/network/tunnel.go b/api/v1/network/tunnel.go index fbdfaf1..7248ff8 100644 --- a/api/v1/network/tunnel.go +++ b/api/v1/network/tunnel.go @@ -1,8 +1,8 @@ package network import ( - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) @@ -33,7 +33,7 @@ type AddNetworkTunnelReq struct { g.Meta `path:"/tunnel/add" method:"post" summary:"添加通道" tags:"网络组件管理"` Name string `json:"name" description:"" v:"required#名称不能为空"` Types string `json:"types" description:"" v:"required#类型不能为空"` - Addr string `json:"addr" description:"" v:"required#地址不能为空"` + Addr string `json:"addr" description:""` Remote string `json:"remote" description:""` Status string `json:"status" description:""` Retry string `json:"retry" description:""` @@ -50,7 +50,7 @@ type EditNetworkTunnelReq struct { Id int `json:"id" description:"id" v:"required#id不能为空"` Name string `json:"name" description:"" v:"required#名称不能为空"` Types string `json:"types" description:"" v:"required#类型不能为空"` - Addr string `json:"addr" description:"" v:"required#地址不能为空"` + Addr string `json:"addr" description:""` Remote string `json:"remote" description:""` Status string `json:"status" description:""` Retry string `json:"retry" description:""` diff --git a/api/v1/notice/config.go b/api/v1/notice/config.go index cbf479f..caeee3c 100644 --- a/api/v1/notice/config.go +++ b/api/v1/notice/config.go @@ -2,10 +2,10 @@ package notice import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" + "sagooiot/api/v1/common" ) -//GetNoticeConfigListReq 获取数据列表 +// GetNoticeConfigListReq 获取数据列表 type GetNoticeConfigListReq struct { g.Meta `path:"/config/list" method:"get" summary:"获取通知配置列表" tags:"通知服务管理"` SendGateway string `json:"sendGateway" description:""` @@ -17,10 +17,10 @@ type GetNoticeConfigListRes struct { common.PaginationRes } -//GetNoticeConfigByIdReq 获取指定ID的数据 +// GetNoticeConfigByIdReq 获取指定ID的数据 type GetNoticeConfigByIdReq struct { g.Meta `path:"/config/get" method:"get" summary:"获取通知配置" tags:"通知服务管理"` - Id int `json:"id" description:"id" v:"required#id不能为空"` + Id string `json:"id" description:"id" v:"required#id不能为空"` } type GetNoticeConfigByIdRes struct { SendGateway string `json:"sendGateway" description:""` @@ -30,7 +30,7 @@ type GetNoticeConfigByIdRes struct { Title string `json:"title" description:""` } -//AddNoticeConfigReq 添加数据 +// AddNoticeConfigReq 添加数据 type AddNoticeConfigReq struct { g.Meta `path:"/config/add" method:"post" summary:"添加通知配置" tags:"通知服务管理"` Title string `json:"title" description:""` @@ -40,7 +40,7 @@ type AddNoticeConfigReq struct { } type AddNoticeConfigRes struct{} -//EditNoticeConfigReq 编辑数据api +// EditNoticeConfigReq 编辑数据api type EditNoticeConfigReq struct { g.Meta `path:"/config/edit" method:"put" summary:"编辑通知配置" tags:"通知服务管理"` Id string `json:"id" description:""` @@ -51,7 +51,7 @@ type EditNoticeConfigReq struct { } type EditNoticeConfigRes struct{} -//DeleteNoticeConfigReq 删除数据 +// DeleteNoticeConfigReq 删除数据 type DeleteNoticeConfigReq struct { g.Meta `path:"/config/delete" method:"delete" summary:"删除通知配置" tags:"通知服务管理"` Ids []string `json:"ids" description:"ids" v:"required#ids不能为空"` diff --git a/api/v1/notice/info.go b/api/v1/notice/info.go index 5182b3b..37e4139 100644 --- a/api/v1/notice/info.go +++ b/api/v1/notice/info.go @@ -2,10 +2,10 @@ package notice import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" + "sagooiot/api/v1/common" ) -//GetNoticeInfoListReq 获取数据列表 +// GetNoticeInfoListReq 获取数据列表 type GetNoticeInfoListReq struct { g.Meta `path:"/info/list" method:"get" summary:"获取通知信息列表" tags:"通知服务管理"` ConfigId string `json:"configId" description:""` @@ -19,7 +19,7 @@ type GetNoticeInfoListRes struct { common.PaginationRes } -//GetNoticeInfoByIdReq 获取指定ID的数据 +// GetNoticeInfoByIdReq 获取指定ID的数据 type GetNoticeInfoByIdReq struct { g.Meta `path:"/info/get" method:"get" summary:"获取通知信息" tags:"通知服务管理"` Id int `json:"id" description:"id" v:"required#id不能为空"` @@ -41,7 +41,7 @@ type GetNoticeInfoByIdRes struct { UserIds string `json:"userIds" description:""` } -//AddNoticeInfoReq 添加数据 +// AddNoticeInfoReq 添加数据 type AddNoticeInfoReq struct { g.Meta `path:"/info/add" method:"post" summary:"添加通知信息" tags:"通知服务管理"` Totag string `json:"totag" description:""` @@ -60,7 +60,7 @@ type AddNoticeInfoReq struct { } type AddNoticeInfoRes struct{} -//EditNoticeInfoReq 编辑数据api +// EditNoticeInfoReq 编辑数据api type EditNoticeInfoReq struct { g.Meta `path:"/info/edit" method:"put" summary:"编辑通知信息" tags:"通知服务管理"` UserIds string `json:"userIds" description:""` @@ -80,7 +80,7 @@ type EditNoticeInfoReq struct { } type EditNoticeInfoRes struct{} -//DeleteNoticeInfoReq 删除数据 +// DeleteNoticeInfoReq 删除数据 type DeleteNoticeInfoReq struct { g.Meta `path:"/info/delete" method:"delete" summary:"删除通知信息" tags:"通知服务管理"` Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` diff --git a/api/v1/notice/log.go b/api/v1/notice/log.go index 0ef1345..fae5434 100644 --- a/api/v1/notice/log.go +++ b/api/v1/notice/log.go @@ -1,7 +1,7 @@ package notice import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/notice/template.go b/api/v1/notice/template.go index 97fecfb..deac760 100644 --- a/api/v1/notice/template.go +++ b/api/v1/notice/template.go @@ -1,12 +1,12 @@ package notice import ( - "github.com/sagoo-cloud/sagooiot/api/v1/common" + "sagooiot/api/v1/common" "github.com/gogf/gf/v2/frame/g" ) -//GetNoticeTemplateListReq 获取数据列表 +// GetNoticeTemplateListReq 获取数据列表 type GetNoticeTemplateListReq struct { g.Meta `path:"/template/list" method:"get" summary:"获取通知模版列表" tags:"通知服务管理"` ConfigId string `json:"configId" description:""` @@ -19,7 +19,7 @@ type GetNoticeTemplateListRes struct { common.PaginationRes } -//GetNoticeTemplateByIdReq 获取指定ID的数据 +// GetNoticeTemplateByIdReq 获取指定ID的数据 type GetNoticeTemplateByIdReq struct { g.Meta `path:"/template/get" method:"get" summary:"获取通知模版" tags:"通知服务管理"` Id string `json:"id" description:"id" v:"required#id不能为空"` @@ -34,7 +34,7 @@ type GetNoticeTemplateByIdRes struct { Id string `json:"id" description:""` } -//GetNoticeTemplateByConfigIdReq 获取指定ConfigId的数据 +// GetNoticeTemplateByConfigIdReq 获取指定ConfigId的数据 type GetNoticeTemplateByConfigIdReq struct { g.Meta `path:"/template/getbyconfig" method:"get" summary:"获取通知模版" tags:"通知服务管理"` ConfigId string `json:"configId" description:"configId" v:"required#通知配置ID不能为空"` @@ -49,7 +49,7 @@ type GetNoticeTemplateByConfigIdRes struct { Id string `json:"id" description:""` } -//AddNoticeTemplateReq 添加数据 +// AddNoticeTemplateReq 添加数据 type AddNoticeTemplateReq struct { g.Meta `path:"/template/add" method:"post" summary:"添加通知模版" tags:"通知服务管理"` Code string `json:"code" description:""` @@ -61,7 +61,7 @@ type AddNoticeTemplateReq struct { } type AddNoticeTemplateRes struct{} -//SaveNoticeTemplateReq 添加数据 +// SaveNoticeTemplateReq 添加数据 type SaveNoticeTemplateReq struct { g.Meta `path:"/template/save" method:"post" summary:"直接更新通知模版数据" tags:"通知服务管理"` Id string `json:"id" description:""` @@ -74,7 +74,7 @@ type SaveNoticeTemplateReq struct { } type SaveNoticeTemplateRes struct{} -//EditNoticeTemplateReq 编辑数据api +// EditNoticeTemplateReq 编辑数据api type EditNoticeTemplateReq struct { g.Meta `path:"/template/edit" method:"put" summary:"编辑通知模版" tags:"通知服务管理"` Id string `json:"id" description:""` @@ -87,7 +87,7 @@ type EditNoticeTemplateReq struct { } type EditNoticeTemplateRes struct{} -//DeleteNoticeTemplateReq 删除数据 +// DeleteNoticeTemplateReq 删除数据 type DeleteNoticeTemplateReq struct { g.Meta `path:"/template/delete" method:"delete" summary:"删除通知模版" tags:"通知服务管理"` Ids []string `json:"ids" description:"ids" v:"required#ids不能为空"` diff --git a/api/v1/product/category.go b/api/v1/product/category.go index f750801..15c82f8 100644 --- a/api/v1/product/category.go +++ b/api/v1/product/category.go @@ -1,8 +1,8 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/product/dev_asset.go b/api/v1/product/dev_asset.go new file mode 100644 index 0000000..1f9f03a --- /dev/null +++ b/api/v1/product/dev_asset.go @@ -0,0 +1,88 @@ +package product + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" +) + +// GetDevAssetListReq 获取数据列表 +type GetDevAssetListReq struct { + g.Meta `path:"/dev_asset/list" method:"get" summary:"获取档案记录列表" tags:"档案管理"` + ProductKey string `json:"productKey" dc:"对应产品key"` // 产品key + common.PaginationReq +} +type GetDevAssetListRes struct { + Data []GetDevAssetByDevKey + common.PaginationRes +} + +type GetDevAssetByDevKey struct { + Id int `json:"id" v:"required#id必填"` + Data []MetaDataValue `json:"data"` + ProductKey string `json:"productKey" description:"产品key"` + DeviceName string `json:"deviceName" description:"设备名称"` + DeviceNumber string `json:"deviceNumber" description:"设备编号"` + DeviceCategory string `json:"deviceCategory" description:"设备类型"` + DeviceKey string `json:"deviceKey" description:"设备key"` + InstallTime string `json:"installTime" description:"安装时间"` + DeptId string `json:"deptId" description:"部门ID"` + Area string `json:"area" description:"所在区域"` +} + +// GetDevAssetByDevKeyReq 获取指定deviceKey的数据 +type GetDevAssetByDevKeyReq struct { + g.Meta `path:"/dev_asset/get" method:"get" summary:"获取档案记录" tags:"档案管理"` + DeviceKey string `json:"deviceKey" description:"设备key" v:"required#deviceKey不能为空"` +} +type GetDevAssetByDevKeyRes struct { + GetDevAssetByDevKey +} + +type MetaDataValue struct { + ProductKey string `json:"productKey" description:"产品标识"` + Name string `json:"name" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + Title string `json:"title" description:"字段标题"` + Value string `json:"value" description:"值"` + FieldName string `json:"fieldName" description:"关联字段名称"` +} + +// AddDevAssetReq 添加数据 +type AddDevAssetReq struct { + g.Meta `path:"/dev_asset/add" method:"post" summary:"添加档案记录" tags:"档案管理"` + Data []MetaDataValue `json:"data"` + ProductKey string `json:"productKey" v:"required#产品key不能为空" description:"产品key"` + DeviceName string `json:"deviceName" v:"required#设备名不能为空" description:"设备名称"` + DeviceNumber string `json:"deviceNumber" description:"设备编号"` + DeviceCategory string `json:"deviceCategory" description:"设备类型"` + DeviceKey string `json:"deviceKey" v:"required#设备key不能为空" description:"设备key"` + InstallTime string `json:"installTime" description:"安装时间"` + DeptId string `json:"deptId" description:"部门ID"` + Area string `json:"area" description:"所在区域"` +} + +type AddDevAssetRes struct{} + +// EditDevAssetReq 编辑数据api +type EditDevAssetReq struct { + g.Meta `path:"/dev_asset/edit" method:"put" summary:"编辑档案记录" tags:"档案管理"` + Id int `json:"id" v:"required#id必填"` + Data []MetaDataValue `json:"data"` + ProductKey string `json:"productKey" description:"产品key"` + DeviceName string `json:"deviceName" description:"设备名称"` + DeviceNumber string `json:"deviceNumber" description:"设备编号"` + DeviceCategory string `json:"deviceCategory" description:"设备类型"` + DeviceKey string `json:"deviceKey" description:"设备key"` + InstallTime string `json:"installTime" description:"安装时间"` + DeptId string `json:"deptId" description:"部门ID"` + Area string `json:"area" description:"所在区域"` +} +type EditDevAssetRes struct{} + +// DeleteDevAssetReq 删除数据 +type DeleteDevAssetReq struct { + g.Meta `path:"/dev_asset/delete" method:"delete" summary:"删除档案记录" tags:"档案管理"` + Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` +} +type DeleteDevAssetRes struct{} diff --git a/api/v1/product/dev_asset_metadata.go b/api/v1/product/dev_asset_metadata.go new file mode 100644 index 0000000..391cbdb --- /dev/null +++ b/api/v1/product/dev_asset_metadata.go @@ -0,0 +1,92 @@ +package product + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" +) + +// GetDevAssetMetadataListReq 获取数据列表 +type GetDevAssetMetadataListReq struct { + g.Meta `path:"/dev_asset_metadata/list" method:"get" summary:"获取档案属性列表" tags:"档案管理"` + KeyWord string `json:"keyWord" dc:"搜索关键字"` //搜索关键字 + ProductKey string `json:"productKey" dc:"对应产品key"` // 产品key + common.PaginationReq +} +type GetDevAssetMetadataListRes struct { + Data []GetDevAssetMetadataByIdRes + common.PaginationRes +} + +// GetDevAssetMetadataByIdReq 获取指定ID的数据 +type GetDevAssetMetadataByIdReq struct { + g.Meta `path:"/dev_asset_metadata/get" method:"get" summary:"获取档案属性" tags:"档案管理"` + Id int `json:"id" description:"id" v:"required#id不能为空"` +} +type GetDevAssetMetadataByIdRes struct { + ProductKey string `json:"productKey" description:"产品标识"` + Name string `json:"name" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + CreatedAt string `json:"createdAt" description:"创建时间"` + DeletedAt string `json:"deletedAt" description:"删除时间"` + Id string `json:"id" description:""` + Title string `json:"title" description:"字段标题"` + FieldName string `json:"fieldName" description:"关联字段名称"` + UpdatedAt string `json:"updatedAt" description:"更新时间"` +} + +// GetDevAssetMetadataByIdReq 获取指定ID的数据 +type GetDevAssetMetadataByProductKeyReq struct { + g.Meta `path:"/dev_asset_metadata/key" method:"get" summary:"获取档案属性" tags:"档案管理"` + ProductKey string `json:"productKey" description:"productKey" v:"required#productKey不能为空"` +} +type GetDevAssetMetadataByProductKeyRes struct { + ProductKey string `json:"productKey" description:"产品标识"` + Name string `json:"name" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + CreatedAt string `json:"createdAt" description:"创建时间"` + DeletedAt string `json:"deletedAt" description:"删除时间"` + Id string `json:"id" description:""` + Title string `json:"title" description:"字段标题"` + FieldName string `json:"fieldName" description:"关联字段名称"` + UpdatedAt string `json:"updatedAt" description:"更新时间"` +} + +// AddDevAssetMetadataReq 添加数据 +type AddDevAssetMetadataReq struct { + g.Meta `path:"/dev_asset_metadata/add" method:"post" summary:"添加档案属性" tags:"档案管理"` + + ProductKey string `json:"productKey" v:"required#产品标识不能为空" description:"产品标识"` + Name string `json:"name" v:"required#字段名称不能为空" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + Title string `json:"title" v:"required#字段标题不能为空" description:"字段标题"` +} +type MetaData struct { + ProductKey string `json:"productKey" description:"产品标识"` + Name string `json:"name" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + Title string `json:"title" description:"字段标题"` +} +type AddDevAssetMetadataRes struct{} + +// EditDevAssetMetadataReq 编辑数据api +type EditDevAssetMetadataReq struct { + g.Meta `path:"/dev_asset_metadata/edit" method:"put" summary:"编辑档案属性" tags:"档案管理"` + Title string `json:"title" v:"required#字段标题不能为空" description:"字段标题"` + Id string `json:"id" description:""` + Name string `json:"name" v:"required#字段名称不能为空" description:"字段名称"` + Desc string `json:"desc" description:"字段描述"` + Types string `json:"types" description:"字段类型"` + ProductKey string `json:"productKey" v:"required#产品标识不能为空" description:"产品标识"` +} +type EditDevAssetMetadataRes struct{} + +// DeleteDevAssetMetadataReq 删除数据 +type DeleteDevAssetMetadataReq struct { + g.Meta `path:"/dev_asset_metadata/delete" method:"delete" summary:"删除档案属性" tags:"档案管理"` + Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` +} +type DeleteDevAssetMetadataRes struct{} diff --git a/api/v1/product/device.go b/api/v1/product/device.go index bff5abf..50d06f8 100644 --- a/api/v1/product/device.go +++ b/api/v1/product/device.go @@ -1,22 +1,17 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + + "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/frame/g" ) -type GetDeviceReq struct { - g.Meta `path:"/device/get" method:"get" summary:"设备详情" tags:"设备"` - Key string `json:"key" dc:"设备标识" v:"required#设备标识不能为空"` -} -type GetDeviceRes struct { - Data *model.DeviceOutput `json:"data" dc:"设备详情"` -} - type DetailDeviceReq struct { - g.Meta `path:"/device/detail" method:"get" summary:"设备详情" tags:"设备"` - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + g.Meta `path:"/device/detail" method:"get" summary:"设备详情" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` } type DetailDeviceRes struct { Data *model.DeviceOutput `json:"data" dc:"设备详情"` @@ -31,8 +26,9 @@ type ListDeviceForPageRes struct { } type ListDeviceReq struct { - g.Meta `path:"/device/list" method:"get" summary:"设备列表" tags:"设备"` - *model.ListDeviceInput + g.Meta `path:"/device/list" method:"get" summary:"已发布产品设备列表" tags:"设备"` + ProductKey string `json:"productKey" dc:"产品Key"` + KeyWord string `json:"keyWord" dc:"搜索设备的关键词"` } type ListDeviceRes struct { Device []*model.DeviceOutput `json:"device" dc:"设备列表"` @@ -50,44 +46,64 @@ type EditDeviceReq struct { } type EditDeviceRes struct{} +type UpdateDeviceExtensionInfoReq struct { + g.Meta `path:"/device/update" method:"put" summary:"更新设备信息" tags:"设备"` + *model.DeviceExtensionInfoInput +} +type UpdateDeviceExtensionInfoRes struct{} + +type UpdateDeviceExtendReq struct { + g.Meta `path:"/device/extend/update" method:"put" summary:"更新设备扩展信息" tags:"设备"` + *model.DeviceExtendInput +} +type UpdateDeviceExtendRes struct{} + type DelDeviceReq struct { g.Meta `path:"/device/del" method:"delete" summary:"删除设备" tags:"设备"` - Ids []uint `json:"ids" dc:"设备Ids" v:"required#设备ID不能为空"` + Keys []string `json:"keys" dc:"设备Keys" v:"required#设备ID不能为空"` } type DelDeviceRes struct{} type DeployDeviceReq struct { - g.Meta `path:"/device/deploy" method:"post" summary:"启用设备" tags:"设备"` - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + g.Meta `path:"/device/deploy" method:"post" summary:"启用设备" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` } type DeployDeviceRes struct{} type UndeployDeviceReq struct { - g.Meta `path:"/device/undeploy" method:"post" summary:"禁用设备" tags:"设备"` - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + g.Meta `path:"/device/undeploy" method:"post" summary:"禁用设备" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` } type UndeployDeviceRes struct{} type OnlineDeviceReq struct { - g.Meta `path:"/device/online" method:"post" summary:"上线设备" tags:"设备"` - Key string `json:"key" dc:"设备标识" v:"required#设备标识不能为空"` + g.Meta `path:"/device/online" method:"post" summary:"上线设备" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` } type OnlineDeviceRes struct{} type OfflineDeviceReq struct { - g.Meta `path:"/device/offline" method:"post" summary:"下线设备" tags:"设备"` - Key string `json:"key" dc:"设备标识" v:"required#设备标识不能为空"` + g.Meta `path:"/device/offline" method:"post" summary:"下线设备" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` } type OfflineDeviceRes struct{} type DeviceRunStatusReq struct { - g.Meta `path:"/device/run_status" method:"get" summary:"运行状态" tags:"设备"` - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + g.Meta `path:"/device/run_status" method:"get" summary:"运行状态" tags:"设备"` + DeviceKey string `json:"device_key" dc:"设备ID" v:"required#设备device_key不能为空"` } type DeviceRunStatusRes struct { *model.DeviceRunStatusOutput } +type DeviceGetLatestPropertyReq struct { + g.Meta `path:"/device/get_latest_property" method:"get" summary:"获取设备最新的属性值" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识deviceKey" v:"required#设备标识不能为空"` +} +type DeviceGetLatestPropertyRes struct { + List []model.DeviceLatestProperty `json:"list" dc:"设备最新的属性值"` +} + type DeviceGetPropertyReq struct { g.Meta `path:"/device/property/get" method:"get" summary:"获取指定属性值" tags:"设备"` *model.DeviceGetPropertyInput @@ -97,8 +113,10 @@ type DeviceGetPropertyRes struct { } type DeviceGetPropertyListReq struct { - g.Meta `path:"/device/property/list" method:"get" summary:"属性详情列表" tags:"设备"` - *model.DeviceGetPropertyListInput + g.Meta `path:"/device/property/list" method:"get" summary:"属性详情列表" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"设备标识"` + PropertyKey string `json:"propertyKey" dc:"属性标识" v:"required#属性标识不能为空"` + common.PaginationReq } type DeviceGetPropertyListRes struct { *model.DeviceGetPropertyListOutput @@ -118,3 +136,72 @@ type DeviceStatisticsForMonthsRes struct { MsgTotal map[int]int `json:"msgTotal" dc:"设备消息量月度统计"` AlarmTotal map[int]int `json:"alarmTotal" dc:"设备告警量月度统计"` } + +type DeviceBindReq struct { + g.Meta `path:"/device/bind_sub" method:"post" summary:"绑定子设备" tags:"设备"` + *model.DeviceBindInput +} +type DeviceBindRes struct{} + +type DeviceUnBindReq struct { + g.Meta `path:"/device/unbind_sub" method:"post" summary:"解绑子设备" tags:"设备"` + *model.DeviceBindInput +} +type DeviceUnBindRes struct{} + +type BindListReq struct { + g.Meta `path:"/device/bind_list" method:"get" summary:"已绑定子设备列表" tags:"设备"` + *model.DeviceBindListInput +} +type BindListRes struct { + *model.DeviceBindListOutput +} + +type ListForSubReq struct { + g.Meta `path:"/device/sub_list" method:"get" summary:"子设备列表" tags:"设备"` + *model.ListForSubInput +} +type ListForSubRes struct { + *model.ListDeviceForPageOutput +} + +type DelSubDeviceReq struct { + g.Meta `path:"/device/del_sub" method:"delete" summary:"删除子设备" tags:"设备"` + DeviceKey string `json:"deviceKey" dc:"子设备标识deviceKey" v:"required#子设备标识不能为空"` +} +type DelSubDeviceRes struct{} + +type ImportDevicesReq struct { + g.Meta `path:"/device/import" method:"post" summary:"导入设备" tags:"设备"` + File *ghttp.UploadFile `json:"file" type:"file" dc:"上传文件" v:"required#请上传文件"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品key不能为空"` +} +type ImportDevicesRes struct { + Success int `json:"success" dc:"导入成功设备数"` + Fail int `json:"fail" dc:"导入失败数"` + DevicesKey []string `json:"deviceKey" dc:"失败的设备标识"` +} + +type ExportDevicesReq struct { + g.Meta `path:"/device/export" method:"get" summary:"导出设备" tags:"设备"` + ProductKey string `json:"productKey" dc:"产品key" v:"required#产品key不能为空"` +} +type ExportDevicesRes struct { + g.Meta `mime:"text/html" example:"string"` +} + +type SetDeviceStatusReq struct { + g.Meta `path:"/device/setDeviceStatus" method:"post" summary:"批量启用/禁用设备" tags:"设备"` + Keys []string `json:"ids" dc:"设备keys" v:"array#设备keys为数组"` + Status int `json:"status" dc:"0禁用,1启用" v:"required#status不能为空"` +} +type SetDeviceStatusRes struct { +} + +type DeviceDataListReq struct { + g.Meta `path:"/device/data/list" method:"get" summary:"获取设备属性数据列表" tags:"设备"` + *model.DeviceDataListInput +} +type DeviceDataListRes struct { + *model.DeviceDataListOutput +} diff --git a/api/v1/product/device_function.go b/api/v1/product/device_function.go new file mode 100644 index 0000000..59b6f53 --- /dev/null +++ b/api/v1/product/device_function.go @@ -0,0 +1,15 @@ +package product + +import ( + "sagooiot/internal/model" + + "github.com/gogf/gf/v2/frame/g" +) + +type DeviceFunctionReq struct { + g.Meta `path:"/function/do" method:"post" summary:"设备功能执行" tags:"设备"` + *model.DeviceFunctionInput +} +type DeviceFunctionRes struct { + *model.DeviceFunctionOutput +} diff --git a/api/v1/product/device_log.go b/api/v1/product/device_log.go index 2f3cb99..5186982 100644 --- a/api/v1/product/device_log.go +++ b/api/v1/product/device_log.go @@ -1,7 +1,7 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/product/device_property.go b/api/v1/product/device_property.go new file mode 100644 index 0000000..fce508f --- /dev/null +++ b/api/v1/product/device_property.go @@ -0,0 +1,15 @@ +package product + +import ( + "sagooiot/internal/model" + + "github.com/gogf/gf/v2/frame/g" +) + +type DevicePropertyReq struct { + g.Meta `path:"/property/set" method:"post" summary:"设备属性设置" tags:"设备"` + *model.DevicePropertyInput +} +type DevicePropertyRes struct { + *model.DevicePropertyOutput +} diff --git a/api/v1/product/device_tag.go b/api/v1/product/device_tag.go index cbb1928..175d799 100644 --- a/api/v1/product/device_tag.go +++ b/api/v1/product/device_tag.go @@ -1,7 +1,7 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/product/device_tree.go b/api/v1/product/device_tree.go new file mode 100644 index 0000000..dc9fd15 --- /dev/null +++ b/api/v1/product/device_tree.go @@ -0,0 +1,47 @@ +package product + +import ( + "sagooiot/internal/model" + + "github.com/gogf/gf/v2/frame/g" +) + +type DeviceTreeListReq struct { + g.Meta `path:"/device_tree/list" method:"get" summary:"设备树列表" tags:"设备树"` +} +type DeviceTreeListRes struct { + List []*model.DeviceTreeListOutput `json:"list" dc:"设备树列表"` +} + +type DeviceTreeChangeReq struct { + g.Meta `path:"/device_tree/change" method:"post" summary:"更换上下级" tags:"设备树"` + InfoId int `json:"infoId" dc:"信息ID" v:"required#信息ID不能为空"` + ParentInfoId int `json:"parentInfoId" dc:"所属信息ID" v:"required#所属信息ID不能为空"` +} +type DeviceTreeChangeRes struct{} + +type DetailDeviceTreeInfoReq struct { + g.Meta `path:"/device_tree/info/detail" method:"get" summary:"信息详情" tags:"设备树"` + InfoId int `json:"infoId" dc:"信息ID" v:"required#信息ID不能为空"` +} +type DetailDeviceTreeInfoRes struct { + Data *model.DetailDeviceTreeInfoOutput `json:"data" dc:"信息详情"` +} + +type AddDeviceTreeInfoReq struct { + g.Meta `path:"/device_tree/info/add" method:"post" summary:"添加设备树基本信息" tags:"设备树"` + *model.AddDeviceTreeInfoInput +} +type AddDeviceTreeInfoRes struct{} + +type EditDeviceTreeInfoReq struct { + g.Meta `path:"/device_tree/info/edit" method:"put" summary:"编辑设备树基本信息" tags:"设备树"` + *model.EditDeviceTreeInfoInput +} +type EditDeviceTreeInfoRes struct{} + +type DelDeviceTreeInfoReq struct { + g.Meta `path:"/device_tree/info/del" method:"delete" summary:"删除设备树基本信息" tags:"设备树"` + Id int `json:"id" dc:"信息ID" v:"required#信息ID不能为空"` +} +type DelDeviceTreeInfoRes struct{} diff --git a/api/v1/product/product.go b/api/v1/product/product.go index ca003d2..4627013 100644 --- a/api/v1/product/product.go +++ b/api/v1/product/product.go @@ -1,23 +1,15 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" ) -type GetProductReq struct { - g.Meta `path:"/get" method:"get" summary:"产品详情" tags:"产品"` - Key string `json:"key" dc:"产品标识" v:"required#产品标识不能为空"` -} -type GetProductRes struct { - Data *model.DetailProductOutput `json:"data" dc:"产品详情"` -} - type DetailProductReq struct { - g.Meta `path:"/detail" method:"get" summary:"产品详情" tags:"产品"` - Id uint `json:"id" dc:"产品ID" v:"required#产品ID不能为空"` + g.Meta `path:"/detail" method:"get" summary:"产品详情" tags:"产品"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } type DetailProductRes struct { Data *model.DetailProductOutput `json:"data" dc:"产品详情"` @@ -50,21 +42,27 @@ type EditProductReq struct { } type EditProductRes struct{} +type UpdateExtendReq struct { + g.Meta `path:"/extend/update" method:"put" summary:"更新产品扩展信息" tags:"产品"` + *model.ExtendInput +} +type UpdateExtendRes struct{} + type DelProductReq struct { g.Meta `path:"/del" method:"delete" summary:"删除产品" tags:"产品"` - Ids []uint `json:"ids" dc:"产品Ids" v:"required#产品ID不能为空"` + Keys []string `json:"keys" dc:"产品Key组" v:"required#产品KEY不能为空"` } type DelProductRes struct{} type DeployProductReq struct { - g.Meta `path:"/deploy" method:"post" summary:"发布产品" tags:"产品"` - Id uint `json:"id" dc:"产品ID" v:"required#产品ID不能为空"` + g.Meta `path:"/deploy" method:"post" summary:"发布产品" tags:"产品"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } type DeployProductRes struct{} type UndeployProductReq struct { - g.Meta `path:"/undeploy" method:"post" summary:"停用产品" tags:"产品"` - Id uint `json:"id" dc:"产品ID" v:"required#产品ID不能为空"` + g.Meta `path:"/undeploy" method:"post" summary:"停用产品" tags:"产品"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } type UndeployProductRes struct{} @@ -75,3 +73,24 @@ type UploadIconReq struct { type UploadIconRes struct { IconPath string `json:"name" dc:"图标地址"` } + +type ListForSubProductReq struct { + g.Meta `path:"/sub_list" method:"get" summary:"子设备类型产品列表" tags:"产品"` +} +type ListForSubProductRes struct { + Product []*model.ProductOutput `json:"product" dc:"子设备类型产品列表"` +} + +type UpdateScriptInfoReq struct { + g.Meta `path:"/script/update" method:"put" summary:"脚本更新" tags:"产品"` + *model.ScriptInfoInput +} +type UpdateScriptInfoRes struct{} + +type ConnectIntroReq struct { + g.Meta `path:"/connect_intro" method:"get" summary:"获取设备接入信息" tags:"产品"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` +} +type ConnectIntroRes struct { + Data *model.DeviceConnectIntroOutput `json:"data" dc:"设备接入信息"` +} diff --git a/api/v1/product/protocol.go b/api/v1/product/protocol.go deleted file mode 100644 index c1f3c71..0000000 --- a/api/v1/product/protocol.go +++ /dev/null @@ -1,23 +0,0 @@ -package product - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -// 消息协议 -type ListMessageProtocolReq struct { - g.Meta `path:"/protocol/message_protocol_list" method:"get" summary:"消息协议" tags:"协议"` -} -type ListMessageProtocolRes struct { - Data []*model.MessageProtocolRes `json:"data" dc:"消息协议"` -} - -// 传输协议 -type ListTrunsportProtocolReq struct { - g.Meta `path:"/protocol/trunsport_protocol_list" method:"get" summary:"传输协议" tags:"协议"` -} -type ListTrunsportProtocolRes struct { - Data []*model.TrunsportProtocolRes `json:"data" dc:"传输协议"` -} diff --git a/api/v1/product/tsl.go b/api/v1/product/tsl.go index 696418b..f56d0f9 100644 --- a/api/v1/product/tsl.go +++ b/api/v1/product/tsl.go @@ -1,7 +1,9 @@ package product import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "github.com/gogf/gf/v2/net/ghttp" + "sagooiot/api/v1/common" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) @@ -18,16 +20,19 @@ type DateTypeRes struct { // 属性 type ListTSLPropertyReq struct { - g.Meta `path:"/tsl/property/list" method:"get" summary:"属性列表" tags:"物模型"` - *model.ListTSLPropertyInput + g.Meta `path:"/tsl/property/list" method:"get" summary:"属性列表" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Name string `json:"name" dc:"属性名称"` + DateType string `json:"dateType" dc:"数据类型"` + common.PaginationReq } type ListTSLPropertyRes struct { *model.ListTSLPropertyOutput } type AllTSLPropertyReq struct { - g.Meta `path:"/tsl/property/all" method:"get" summary:"所有属性列表" tags:"物模型"` - Key string `json:"key" dc:"产品标识" v:"required#产品标识不能为空"` + g.Meta `path:"/tsl/property/all" method:"get" summary:"所有属性列表" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } type AllTSLPropertyRes struct { Data []model.TSLProperty @@ -46,21 +51,32 @@ type EditTSLPropertyReq struct { type EditTSLPropertyRes struct{} type DelTSLPropertyReq struct { - g.Meta `path:"/tsl/property/del" method:"delete" summary:"属性删除" tags:"物模型"` - *model.DelTSLPropertyInput + g.Meta `path:"/tsl/property/del" method:"delete" summary:"属性删除" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Key string `json:"key" dc:"属性标识" v:"required#属性标识不能为空"` } type DelTSLPropertyRes struct{} // 功能 type ListTSLFunctionReq struct { - g.Meta `path:"/tsl/function/list" method:"get" summary:"功能列表" tags:"物模型"` - *model.ListTSLFunctionInput + g.Meta `path:"/tsl/function/list" method:"get" summary:"功能列表" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` + common.PaginationReq } type ListTSLFunctionRes struct { *model.ListTSLFunctionOutput } +type AllTSLFunctionReq struct { + g.Meta `path:"/tsl/function/all" method:"get" summary:"所有功能列表" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` + InputsValueTypes string `json:"inputsValueTypes" dc:"参数值类型"` +} +type AllTSLFunctionRes struct { + Data []model.TSLFunction +} + type AddTSLFunctionReq struct { g.Meta `path:"/tsl/function/add" method:"post" summary:"功能添加" tags:"物模型"` *model.TSLFunctionAddInput @@ -80,7 +96,6 @@ type DelTSLFunctionReq struct { type DelTSLFunctionRes struct{} // 事件 - type ListTSLEventReq struct { g.Meta `path:"/tsl/event/list" method:"get" summary:"事件列表" tags:"物模型"` *model.ListTSLEventInput @@ -89,15 +104,23 @@ type ListTSLEventRes struct { *model.ListTSLEventOutput } +type AllTSLEventReq struct { + g.Meta `path:"/tsl/event/all" method:"get" summary:"所有事件列表" tags:"物模型"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` +} +type AllTSLEventRes struct { + Data []model.TSLEvent +} + type AddTSLEventReq struct { g.Meta `path:"/tsl/event/add" method:"post" summary:"事件添加" tags:"物模型"` - *model.TSLEventInput + *model.TSLEventAddInput } type AddTSLEventRes struct{} type EditTSLEventReq struct { g.Meta `path:"/tsl/event/edit" method:"put" summary:"事件编辑" tags:"物模型"` - *model.TSLEventInput + *model.TSLEventAddInput } type EditTSLEventRes struct{} @@ -118,28 +141,36 @@ type ListTSLTagRes struct { } type AddTSLTagReq struct { - g.Meta `path:"/tsl/tag/add" method:"post" summary:"标签添加" tags:"物模型"` + g.Meta `path:"/tsl/tag/add" method:"post" summary:"物模型标签添加" tags:"物模型"` *model.TSLTagInput } type AddTSLTagRes struct{} type EditTSLTagReq struct { - g.Meta `path:"/tsl/tag/edit" method:"put" summary:"标签编辑" tags:"物模型"` + g.Meta `path:"/tsl/tag/edit" method:"put" summary:"物模型标签编辑" tags:"物模型"` *model.TSLTagInput } type EditTSLTagRes struct{} type DelTSLTagReq struct { - g.Meta `path:"/tsl/tag/del" method:"delete" summary:"标签删除" tags:"物模型"` + g.Meta `path:"/tsl/tag/del" method:"delete" summary:"物模型标签删除" tags:"物模型"` *model.DelTSLTagInput } type DelTSLTagRes struct{} -type AllTSLFunctionReq struct { - g.Meta `path:"/tsl/function/all" method:"get" summary:"所有功能列表" tags:"物模型"` - Key string `json:"key" dc:"产品标识" v:"required#产品标识不能为空"` - InputsValueTypes string `json:"inputsValueTypes" dc:"参数值类型"` +// ExportTSLReq 导出物模型 +type ExportTSLReq struct { + g.Meta `path:"/tsl/export" method:"get" summary:"导出物模型" tags:"物模型" ` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } -type AllTSLFunctionRes struct { - Data []model.TSLFunction +type ExportTSLRes struct { + g.Meta `mime:"text/html" example:"string"` +} + +// ImportTSLReq 导入物模型 +type ImportTSLReq struct { + g.Meta `path:"/tsl/import" method:"post" summary:"导入物模型" tags:"物模型" ` + File *ghttp.UploadFile `json:"file" type:"file" dc:"上传文件" v:"required#请上传文件"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品标识不能为空"` } +type ImportTSLRes struct{} diff --git a/api/v1/source/node.go b/api/v1/source/node.go deleted file mode 100644 index 1963d7f..0000000 --- a/api/v1/source/node.go +++ /dev/null @@ -1,33 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataNodeAddReq struct { - g.Meta `path:"/node/add" method:"post" summary:"添加数据节点" tags:"数据源"` - *model.DataNodeAddInput -} -type DataNodeAddRes struct{} - -type DataNodeEditReq struct { - g.Meta `path:"/node/edit" method:"put" summary:"编辑数据节点" tags:"数据源"` - *model.DataNodeEditInput -} -type DataNodeEditRes struct{} - -type DataNodeDelReq struct { - g.Meta `path:"/node/del" method:"delete" summary:"删除数据节点" tags:"数据源"` - NodeId uint64 `json:"nodeId" dc:"数据节点ID" v:"required#数据节点ID不能为空"` -} -type DataNodeDelRes struct{} - -type DataNodeListReq struct { - g.Meta `path:"/node/list" method:"get" summary:"数据节点列表" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataNodeListRes struct { - List []*model.DataNodeOutput `json:"list" dc:"数据节点列表"` -} diff --git a/api/v1/source/source.go b/api/v1/source/source.go deleted file mode 100644 index ca8db03..0000000 --- a/api/v1/source/source.go +++ /dev/null @@ -1,83 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataSourceApiAddReq struct { - g.Meta `path:"/api/add" method:"post" summary:"添加API数据源" tags:"数据源"` - *model.DataSourceApiAddInput -} -type DataSourceApiAddRes struct{} - -type DataSourceApiEditReq struct { - g.Meta `path:"/api/edit" method:"put" summary:"编辑API数据源" tags:"数据源"` - *model.DataSourceApiEditInput -} -type DataSourceApiEditRes struct{} - -type DataSourceApiGetReq struct { - g.Meta `path:"/api/get" method:"get" summary:"获取API数据" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceApiGetRes struct { - Data string `json:"data" dc:"api源数据"` -} - -type DataSourceDelReq struct { - g.Meta `path:"/del" method:"delete" summary:"删除数据源" tags:"数据源"` - Ids []uint64 `json:"ids" dc:"数据源Ids" v:"required#数据源ID不能为空"` -} -type DataSourceDelRes struct{} - -type DataSourceSearchReq struct { - g.Meta `path:"/search" method:"get" summary:"搜索数据源" tags:"数据源"` - *model.DataSourceSearchInput -} -type DataSourceSearchRes struct { - *model.DataSourceSearchOutput -} - -type DataSourceListReq struct { - g.Meta `path:"/list" method:"get" summary:"数据源列表" tags:"数据源"` -} -type DataSourceListRes struct { - List []*entity.DataSource `json:"list" dc:"数据源列表"` -} - -type DataSourceReq struct { - g.Meta `path:"/detail" method:"get" summary:"数据源详情" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceRes struct { - Data *model.DataSourceOutput `json:"data" dc:"数据源详情"` -} - -type DataSourceDeployReq struct { - g.Meta `path:"/deploy" method:"post" summary:"发布" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceDeployRes struct{} - -type DataSourceUndeployReq struct { - g.Meta `path:"/undeploy" method:"post" summary:"停用" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceUndeployRes struct{} - -type DataSourceDataReq struct { - g.Meta `path:"/getdata" method:"get" summary:"获取源数据记录" tags:"数据源"` - *model.DataSourceDataInput -} -type DataSourceDataRes struct { - *model.DataSourceDataOutput -} - -type DataSourceCopyReq struct { - g.Meta `path:"/copy" method:"post" summary:"复制数据源" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceCopyRes struct{} diff --git a/api/v1/source/source_db.go b/api/v1/source/source_db.go deleted file mode 100644 index c62fa0c..0000000 --- a/api/v1/source/source_db.go +++ /dev/null @@ -1,35 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataSourceDbAddReq struct { - g.Meta `path:"/db/add" method:"post" summary:"添加数据库数据源" tags:"数据源"` - *model.DataSourceDbAddInput -} -type DataSourceDbAddRes struct{} - -type DataSourceDbEditReq struct { - g.Meta `path:"/db/edit" method:"put" summary:"编辑数据库数据源" tags:"数据源"` - *model.DataSourceDbEditInput -} -type DataSourceDbEditRes struct{} - -type DataSourceDbGetReq struct { - g.Meta `path:"/db/get" method:"get" summary:"获取数据库数据" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceDbGetRes struct { - Data string `json:"data" dc:"数据库源数据"` -} - -type DataSourceDbFieldsReq struct { - g.Meta `path:"/db/fields" method:"get" summary:"获取数据表字段" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceDbFieldsRes struct { - Data g.MapStrAny `json:"data" dc:"数据表字段"` -} diff --git a/api/v1/source/source_device.go b/api/v1/source/source_device.go deleted file mode 100644 index 0f173f4..0000000 --- a/api/v1/source/source_device.go +++ /dev/null @@ -1,27 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataSourceDeviceAddReq struct { - g.Meta `path:"/device/add" method:"post" summary:"添加设备数据源" tags:"数据源"` - *model.DataSourceDeviceAddInput -} -type DataSourceDeviceAddRes struct{} - -type DataSourceDeviceEditReq struct { - g.Meta `path:"/device/edit" method:"put" summary:"编辑设备数据源" tags:"数据源"` - *model.DataSourceDeviceEditInput -} -type DataSourceDeviceEditRes struct{} - -type DataSourceDeviceGetReq struct { - g.Meta `path:"/device/get" method:"get" summary:"获取设备数据" tags:"数据源"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` -} -type DataSourceDeviceGetRes struct { - Data string `json:"data" dc:"设备源数据"` -} diff --git a/api/v1/source/template.go b/api/v1/source/template.go deleted file mode 100644 index 8631898..0000000 --- a/api/v1/source/template.go +++ /dev/null @@ -1,97 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataTemplateAddReq struct { - g.Meta `path:"/template/add" method:"post" summary:"添加数据模型" tags:"数据建模"` - *model.DataTemplateAddInput -} -type DataTemplateAddRes struct{} - -type DataTemplateEditReq struct { - g.Meta `path:"/template/edit" method:"put" summary:"编辑数据模型" tags:"数据建模"` - *model.DataTemplateEditInput -} -type DataTemplateEditRes struct{} - -type DataTemplateDelReq struct { - g.Meta `path:"/template/del" method:"delete" summary:"删除数据模型" tags:"数据建模"` - Ids []uint64 `json:"ids" dc:"数据模型Ids" v:"required#数据模型ID不能为空"` -} -type DataTemplateDelRes struct{} - -type DataTemplateSearchReq struct { - g.Meta `path:"/template/search" method:"get" summary:"搜索数据模型" tags:"数据建模"` - *model.DataTemplateSearchInput -} -type DataTemplateSearchRes struct { - *model.DataTemplateSearchOutput -} - -type DataTemplateListReq struct { - g.Meta `path:"/template/list" method:"get" summary:"已发布数据模型列表" tags:"数据建模"` -} -type DataTemplateListRes struct { - List []*entity.DataTemplate `json:"list" dc:"数据模型列表"` -} - -type DataTemplateReq struct { - g.Meta `path:"/template/detail" method:"get" summary:"数据模型详情" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateRes struct { - Data *model.DataTemplateOutput `json:"data" dc:"数据模型详情"` -} - -type DataTemplateDeployReq struct { - g.Meta `path:"/template/deploy" method:"post" summary:"发布" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateDeployRes struct{} - -type DataTemplateUndeployReq struct { - g.Meta `path:"/template/undeploy" method:"post" summary:"停用" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateUndeployRes struct{} - -type DataTemplateDataReq struct { - g.Meta `path:"/template/getdata" method:"get" summary:"获取模型数据" tags:"数据建模"` - *model.DataTemplateDataInput -} -type DataTemplateDataRes struct { - *model.DataTemplateDataOutput -} - -type DataTemplateCopyReq struct { - g.Meta `path:"/template/copy" method:"post" summary:"复制模型" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateCopyRes struct{} - -type DataTemplateCheckRelationReq struct { - g.Meta `path:"/template/relation_check" method:"get" summary:"检测数据模型是否需要设置关联" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateCheckRelationRes struct { - Yes bool `json:"yes" dc:"是否需要设置关联: true:需要设置, false:不需要"` -} - -type DataTemplateRelationReq struct { - g.Meta `path:"/template/relation" method:"post" summary:"设置主源、关联字段" tags:"数据建模"` - *model.TemplateDataRelationInput -} -type DataTemplateRelationRes struct{} - -type TemplateSourceListReq struct { - g.Meta `path:"/template/source_list" method:"get" summary:"数据模型源列表" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type TemplateSourceListRes struct { - List []*model.DataSourceOutput -} diff --git a/api/v1/source/template_node.go b/api/v1/source/template_node.go deleted file mode 100644 index 87868aa..0000000 --- a/api/v1/source/template_node.go +++ /dev/null @@ -1,33 +0,0 @@ -package source - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type DataTemplateNodeAddReq struct { - g.Meta `path:"/template/node/add" method:"post" summary:"添加数据模型节点" tags:"数据建模"` - *model.DataTemplateNodeAddInput -} -type DataTemplateNodeAddRes struct{} - -type DataTemplateNodeEditReq struct { - g.Meta `path:"/template/node/edit" method:"put" summary:"编辑数据模型节点" tags:"数据建模"` - *model.DataTemplateNodeEditInput -} -type DataTemplateNodeEditRes struct{} - -type DataTemplateNodeDelReq struct { - g.Meta `path:"/template/node/del" method:"delete" summary:"删除数据模型节点" tags:"数据建模"` - Id uint64 `json:"id" dc:"数据模型节点ID" v:"required#数据模型节点ID不能为空"` -} -type DataTemplateNodeDelRes struct{} - -type DataTemplateNodeListReq struct { - g.Meta `path:"/template/node/list" method:"get" summary:"数据模型节点列表" tags:"数据建模"` - Tid uint64 `json:"tid" dc:"数据模型ID" v:"required#数据模型ID不能为空"` -} -type DataTemplateNodeListRes struct { - List []*model.DataTemplateNodeOutput `json:"list" dc:"数据模型节点列表"` -} diff --git a/api/v1/system/login.go b/api/v1/system/login.go index 2fe5366..652a0b4 100644 --- a/api/v1/system/login.go +++ b/api/v1/system/login.go @@ -2,7 +2,7 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type LoginDoReq struct { @@ -13,8 +13,9 @@ type LoginDoReq struct { VerifyKey string `json:"verifyKey"` } type LoginDoRes struct { - UserInfo *model.LoginUserRes `json:"userInfo"` - Token string `json:"token"` + UserInfo *model.LoginUserRes `json:"userInfo"` + Token string `json:"token"` + IsChangePwd int `json:"isChangePwd" dc:"是否需要变更密码, 1是 0 否"` } type LoginOutReq struct { @@ -22,3 +23,12 @@ type LoginOutReq struct { } type LoginOutRes struct { } + +type EditPasswordReq struct { + g.Meta `path:"/user/editPassword" method:"post" summary:"用户修改密码" tags:"登录"` + UserName string `json:"userName" description:"用户账户" v:"required#账户不能为空"` + OldUserPassword string `json:"oldUserPassword" description:"原密码" v:"required#原密码不能为空"` + UserPassword string `json:"userPassword" description:"登录密码;cmf_password加密" v:"required#新密码不能为空"` +} +type EditPasswordRes struct { +} diff --git a/api/v1/system/sys_api.go b/api/v1/system/sys_api.go index 5dcade8..b7b1fa8 100644 --- a/api/v1/system/sys_api.go +++ b/api/v1/system/sys_api.go @@ -2,11 +2,12 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type GetApiAllReq struct { g.Meta `path:"/api/GetAll" method:"get" summary:"获取所有接口" tags:"接口API管理"` + Method string `json:"method" description:"请求方式(数据字典维护)"` } type GetApiAllRes struct { Data []*model.SysApiAllRes @@ -28,6 +29,7 @@ type AddApiReq struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称" v:"required#请输入名称"` Types int `json:"types" description:"1 分类 2接口" v:"required#请选择类型"` + ApiTypes string `json:"apiTypes" description:"数据字典维护" v:"required#请选择接口类型"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -45,6 +47,7 @@ type EditApiReq struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称" v:"required#请输入名称"` Types int `json:"types" description:"1 分类 2接口" v:"required#请选择类型"` + ApiTypes string `json:"apiTypes" description:"数据字典维护" v:"required#请选择接口类型"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址" ` Remark string `json:"remark" description:"备注"` @@ -77,3 +80,16 @@ type EditApiStatusReq struct { } type EditApiStatusRes struct { } + +type ImportApiFileReq struct { + g.Meta `path:"/api/import" method:"post" summary:"导入Api文件" tags:"接口API管理"` +} +type ImportApiFileRes struct { +} + +type BindApiMenusReq struct { + g.Meta `path:"/api/bindMenus" method:"post" summary:"批量绑定菜单" tags:"接口API管理"` + BindMenus []*model.BindMenusReq `json:"bindMenus" description:"接口ID"` +} +type BindApiMenusRes struct { +} diff --git a/api/v1/system/sys_authorize.go b/api/v1/system/sys_authorize.go index 3c44970..3af4b81 100644 --- a/api/v1/system/sys_authorize.go +++ b/api/v1/system/sys_authorize.go @@ -2,7 +2,7 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type AuthorizeQueryReq struct { diff --git a/api/v1/system/sys_blacklist.go b/api/v1/system/sys_blacklist.go new file mode 100644 index 0000000..09d368c --- /dev/null +++ b/api/v1/system/sys_blacklist.go @@ -0,0 +1,68 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" +) + +// GetBlacklistListReq 获取数据列表 +type GetBlacklistListReq struct { + g.Meta `path:"/blacklist/list" method:"get" summary:"获取黑名单列表" tags:"黑名单管理"` + common.PaginationReq +} +type GetBlacklistListRes struct { + Data []GetBlacklistByIdRes + common.PaginationRes +} + +// GetBlacklistByIdReq 获取指定ID的数据 +type GetBlacklistByIdReq struct { + g.Meta `path:"/blacklist/get" method:"get" summary:"获取黑名单" tags:"黑名单管理"` + Id int `json:"id" description:"id" v:"required#id不能为空"` +} +type GetBlacklistByIdRes struct { + UpdatedAt string `json:"updatedAt" description:"更新时间"` + Id string `json:"id" description:"黑名单ID"` + Ip string `json:"ip" description:"IP地址"` + Remark string `json:"remark" description:"备注"` + Status string `json:"status" description:"状态"` + CreatedAt string `json:"createdAt" description:"创建时间"` +} + +// AddBlacklistReq 添加数据 +type AddBlacklistReq struct { + g.Meta `path:"/blacklist/add" method:"post" summary:"添加黑名单" tags:"黑名单管理"` + Ip string `json:"ip" description:"IP地址"` + Status string `json:"status" description:"状态"` + Remark string `json:"remark" description:"备注"` + CreatedAt string `json:"createdAt" description:"创建时间"` + UpdatedAt string `json:"updatedAt" description:"更新时间"` +} +type AddBlacklistRes struct{} + +// EditBlacklistReq 编辑数据api +type EditBlacklistReq struct { + g.Meta `path:"/blacklist/edit" method:"put" summary:"编辑黑名单" tags:"黑名单管理"` + Id string `json:"id" description:"黑名单ID"` + Ip string `json:"ip" description:"IP地址"` + Remark string `json:"remark" description:"备注"` + Status string `json:"status" description:"状态"` + CreatedAt string `json:"createdAt" description:"创建时间"` + UpdatedAt string `json:"updatedAt" description:"更新时间"` +} +type EditBlacklistRes struct{} + +// DeleteBlacklistReq 删除数据 +type DeleteBlacklistReq struct { + g.Meta `path:"/blacklist/delete" method:"delete" summary:"删除黑名单" tags:"黑名单管理"` + Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` +} +type DeleteBlacklistRes struct{} + +// StatusReq 更新状态 +type StatusReq struct { + g.Meta `path:"/blacklist/status" method:"post" summary:"更新黑名单状态" tags:"黑名单管理"` + Id int `json:"id" description:"黑名单ID"` + Status int `json:"status" description:"状态"` +} +type StatusRes struct{} diff --git a/api/v1/system/sys_certificate.go b/api/v1/system/sys_certificate.go new file mode 100644 index 0000000..b66db74 --- /dev/null +++ b/api/v1/system/sys_certificate.go @@ -0,0 +1,76 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" + "sagooiot/internal/model" +) + +// GetCertificateListReq 获取数据列表 +type GetCertificateListReq struct { + g.Meta `path:"/certificate/list" method:"get" summary:"获取列表" tags:"证书管理"` + Name string `json:"name" description:"名称"` + Status int `json:"status" description:"状态 0 停用 1启用"` + common.PaginationReq +} +type GetCertificateListRes struct { + Info []model.SysCertificateListRes + common.PaginationRes +} + +// GetCertificateByIdReq 获取指定ID的数据 +type GetCertificateByIdReq struct { + g.Meta `path:"/certificate/getById" method:"get" summary:"根据ID获取数据" tags:"证书管理"` + Id int `json:"id" description:"id" v:"required#id不能为空"` +} +type GetCertificateByIdRes struct { + Info *model.SysCertificateOut +} + +// AddCertificateReq 添加数据 +type AddCertificateReq struct { + g.Meta `path:"/certificate/add" method:"post" summary:"添加证书" tags:"证书管理"` + Name string `json:"name" description:"名称" v:"required#名称不能为空"` + Standard string `json:"standard" description:"证书标准" v:"required#证书标准不能为空"` + FileContent string `json:"fileContent" description:"证书文件内容" v:"required#证书内容不能为空"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容" v:"required#证书内容不能为空"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容" v:"required#证书私钥内容不能为空"` + Description string `json:"description" description:"说明"` +} +type AddCertificateRes struct{} + +// EditCertificateReq 编辑数据 +type EditCertificateReq struct { + g.Meta `path:"/certificate/edit" method:"put" summary:"编辑证书" tags:"证书管理"` + Id int `json:"id" description:"" v:"required#id不能为空"` + Name string `json:"name" description:"名称" v:"required#名称不能为空"` + Standard string `json:"standard" description:"证书标准" v:"required#证书标准不能为空"` + FileContent string `json:"fileContent" description:"证书文件内容" v:"required#证书内容不能为空"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容" v:"required#证书内容不能为空"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容" v:"required#证书私钥内容不能为空"` + Description string `json:"description" description:"说明"` +} +type EditCertificateRes struct{} + +// DeleteCertificateReq 删除数据 +type DeleteCertificateReq struct { + g.Meta `path:"/certificate/delete" method:"delete" summary:"删除证书" tags:"证书管理"` + Id int `json:"id" description:"id" v:"required#id不能为空"` +} +type DeleteCertificateRes struct{} + +// EditCertificateStatusReq 更新状态 +type EditCertificateStatusReq struct { + g.Meta `path:"/certificate/editStatus" method:"post" summary:"更新证书状态" tags:"证书管理"` + Id int `json:"id" description:"id" v:"required#id不能为空"` + Status int `json:"status" description:"状态" v:"required#状态不能为空"` +} +type EditCertificateStatusRes struct{} + +// GetCertificateAllReq 获取所有证书 +type GetCertificateAllReq struct { + g.Meta `path:"/certificate/getAll" method:"get" summary:"获取所有证书" tags:"证书管理"` +} +type GetCertificateAllRes struct { + Info []*model.SysCertificateOut +} diff --git a/api/v1/system/sys_dept.go b/api/v1/system/sys_dept.go index 42d5622..b71cbb2 100644 --- a/api/v1/system/sys_dept.go +++ b/api/v1/system/sys_dept.go @@ -1,7 +1,7 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/system/sys_job.go b/api/v1/system/sys_job.go index 54b7926..97f19f3 100644 --- a/api/v1/system/sys_job.go +++ b/api/v1/system/sys_job.go @@ -2,8 +2,8 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" ) type GetJobListReq struct { @@ -18,6 +18,13 @@ type GetJobListRes struct { common.PaginationRes } +type GetJobFunListReq struct { + g.Meta `path:"/job/fun_list" method:"get" summary:"获取任务可用方法列表" tags:"定时任务管理"` +} +type GetJobFunListRes struct { + Data []*model.SysJobFunListOut +} + type AddJobReq struct { g.Meta `path:"/job/add" method:"post" summary:"添加定时任务" tags:"定时任务管理"` JobName string `json:"jobName" description:"任务名称" v:"required#任务名称不能来空"` diff --git a/api/v1/system/sys_login_log.go b/api/v1/system/sys_login_log.go index 805d379..165e093 100644 --- a/api/v1/system/sys_login_log.go +++ b/api/v1/system/sys_login_log.go @@ -1,9 +1,9 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/system/sys_menu.go b/api/v1/system/sys_menu.go index 1b9894d..0db43ae 100644 --- a/api/v1/system/sys_menu.go +++ b/api/v1/system/sys_menu.go @@ -1,7 +1,7 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) @@ -78,133 +78,3 @@ type DelMenuReq struct { } type DelMenuRes struct { } - -//********** 菜单按钮关联开始 ********** - -type MenuButtonDoReq struct { - g.Meta `path:"/menu/button/tree" tags:"菜单管理" method:"get" summary:"菜单与按钮树列表"` - MenuId int `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` - ParentId int `json:"parentId" dc:"父ID"` - Status int `json:"status" dc:"状态:-1为全部,0为正常,1为停用"` - Name string `json:"name" dc:"名称"` -} -type MenuButtonDoRes struct { - Data []*model.UserMenuButtonRes -} - -type AddMenuButtonReq struct { - g.Meta `path:"/menu/button/add" tags:"菜单管理" method:"post" summary:"添加菜单与按钮相关关联"` - ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择菜单ID"` - Name string `json:"name" description:"名称" v:"required#请输入名称"` - Types string `json:"types" description:"类型 自定义 add添加 edit编辑 del 删除" v:"required#请选择类型"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type AddMenuButtonRes struct { -} - -type DetailMenuButtonReq struct { - g.Meta `path:"/menu/button/detail" tags:"菜单管理" method:"get" summary:"根据ID获取菜单按钮详情"` - Id int64 `p:"id" description:"菜单按钮ID" v:"required#ID不能为空"` -} -type DetailMenuButtonRes struct { - Data *model.DetailMenuButtonRes -} - -type EditMenuButtonReq struct { - g.Meta `path:"/menu/button/edit" method:"put" summary:"编辑菜单按钮" tags:"菜单管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` - ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` - Name string `json:"name" description:"名称" v:"required#请输入名称"` - Types string `json:"types" description:"类型 自定义 add添加 edit编辑 del 删除" v:"required#请选择类型"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type EditMenuButtonRes struct { -} - -type DelMenuButtonReq struct { - g.Meta `path:"/menu/button/del" method:"delete" summary:"根据ID删除菜单按钮" tags:"菜单管理"` - Id int64 `json:"id" description:"菜单按钮ID" v:"required#ID不能为空"` -} -type DelMenuButtonRes struct { -} - -type EditMenuButtonStatusReq struct { - g.Meta `path:"/menu/button/editStatus" method:"put" summary:"编辑菜单按钮状态" tags:"菜单管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} - -type EditMenuButtonStatusRes struct { -} - -//********** 菜单按钮关联结束 ********** - -//********** 菜单列表关联开始 ********** - -type MenuColumnDoReq struct { - g.Meta `path:"/menu/column/tree" tags:"菜单管理" method:"get" summary:"菜单与列表树列表"` - MenuId string `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` - ParentId string `json:"parentId" dc:"父ID"` - Status int `json:"status" dc:"状态:-1为全部,0为正常,1为停用"` - Name string `json:"name" dc:"名称"` -} -type MenuColumnDoRes struct { - Data []*model.UserMenuColumnRes -} - -type AddMenuColumnReq struct { - g.Meta `path:"/menu/column/add" tags:"菜单管理" method:"post" summary:"添加菜单与列表相关关联"` - ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择菜单ID"` - Name string `json:"name" description:"名称" v:"required#请输入名称"` - Code string `json:"code" description:"代表列表"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type AddMenuColumnRes struct { -} - -type DetailMenuColumnReq struct { - g.Meta `path:"/menu/column/detail" tags:"菜单管理" method:"get" summary:"根据ID获取菜单列表详情"` - Id int64 `p:"id" description:"菜单列表ID" v:"required#ID不能为空"` -} -type DetailMenuColumnRes struct { - Data *model.DetailMenuColumnRes -} - -type EditMenuColumnReq struct { - g.Meta `path:"/menu/column/edit" method:"put" summary:"编辑菜单列表" tags:"菜单管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` - ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` - Name string `json:"name" description:"名称" v:"required#请输入名称"` - Code string `json:"code" description:"代表列表"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} -type EditMenuColumnRes struct { -} - -type DelMenuColumnReq struct { - g.Meta `path:"/menu/column/del" method:"delete" summary:"根据ID删除菜单列表" tags:"菜单管理"` - Id int64 `p:"id" description:"菜单列表ID" v:"required#ID不能为空"` -} -type DelMenuColumnRes struct { -} - -type EditMenuColumnStatusReq struct { - g.Meta `path:"/menu/column/editStatus" method:"put" summary:"编辑菜单列表状态" tags:"菜单管理"` - Id int `json:"id" description:"" v:"required#ID不能为空"` - MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} - -type EditMenuColumnStatusRes struct { -} - -//********** 菜单列表关联结束 ********** diff --git a/api/v1/system/sys_menu_api.go b/api/v1/system/sys_menu_api.go new file mode 100644 index 0000000..1fc70fb --- /dev/null +++ b/api/v1/system/sys_menu_api.go @@ -0,0 +1,22 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" +) + +type MenuApiDoReq struct { + g.Meta `path:"/menu/api/tree" tags:"菜单管理" method:"get" summary:"菜单与API列表"` + MenuId int `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` +} +type MenuApiDoRes struct { + Data []*model.SysApiAllRes +} + +type AddMenuApiReq struct { + g.Meta `path:"/menu/api/add" tags:"菜单管理" method:"post" summary:"绑定菜单和API关联关系"` + MenuId int `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` + ApiIds []int `json:"apiIds" dc:"API ID"` +} +type AddMenuApiRes struct { +} diff --git a/api/v1/system/sys_menu_button.go b/api/v1/system/sys_menu_button.go new file mode 100644 index 0000000..041cf02 --- /dev/null +++ b/api/v1/system/sys_menu_button.go @@ -0,0 +1,67 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" +) + +type MenuButtonDoReq struct { + g.Meta `path:"/menu/button/tree" tags:"菜单管理" method:"get" summary:"菜单与按钮树列表"` + MenuId int `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` + ParentId int `json:"parentId" dc:"父ID"` + Status int `json:"status" dc:"状态:-1为全部,0为正常,1为停用"` + Name string `json:"name" dc:"名称"` +} +type MenuButtonDoRes struct { + Data []*model.UserMenuButtonRes +} + +type AddMenuButtonReq struct { + g.Meta `path:"/menu/button/add" tags:"菜单管理" method:"post" summary:"添加菜单与按钮相关关联"` + ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择菜单ID"` + Name string `json:"name" description:"名称" v:"required#请输入名称"` + Types string `json:"types" description:"类型 自定义 add添加 edit编辑 del 删除" v:"required#请选择类型"` + Description string `json:"description" description:"描述"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} +type AddMenuButtonRes struct { +} + +type DetailMenuButtonReq struct { + g.Meta `path:"/menu/button/detail" tags:"菜单管理" method:"get" summary:"根据ID获取菜单按钮详情"` + Id int64 `p:"id" description:"菜单按钮ID" v:"required#ID不能为空"` +} +type DetailMenuButtonRes struct { + Data *model.DetailMenuButtonRes +} + +type EditMenuButtonReq struct { + g.Meta `path:"/menu/button/edit" method:"put" summary:"编辑菜单按钮" tags:"菜单管理"` + Id int `json:"id" description:"" v:"required#ID不能为空"` + ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` + Name string `json:"name" description:"名称" v:"required#请输入名称"` + Types string `json:"types" description:"类型 自定义 add添加 edit编辑 del 删除" v:"required#请选择类型"` + Description string `json:"description" description:"描述"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} +type EditMenuButtonRes struct { +} + +type DelMenuButtonReq struct { + g.Meta `path:"/menu/button/del" method:"delete" summary:"根据ID删除菜单按钮" tags:"菜单管理"` + Id int64 `json:"id" description:"菜单按钮ID" v:"required#ID不能为空"` +} +type DelMenuButtonRes struct { +} + +type EditMenuButtonStatusReq struct { + g.Meta `path:"/menu/button/editStatus" method:"put" summary:"编辑菜单按钮状态" tags:"菜单管理"` + Id int `json:"id" description:"" v:"required#ID不能为空"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} + +type EditMenuButtonStatusRes struct { +} diff --git a/api/v1/system/sys_menu_column.go b/api/v1/system/sys_menu_column.go new file mode 100644 index 0000000..2b73849 --- /dev/null +++ b/api/v1/system/sys_menu_column.go @@ -0,0 +1,67 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" +) + +type MenuColumnDoReq struct { + g.Meta `path:"/menu/column/tree" tags:"菜单管理" method:"get" summary:"菜单与列表树列表"` + MenuId string `json:"menuId" dc:"菜单ID" v:"required#菜单ID不能为空"` + ParentId string `json:"parentId" dc:"父ID"` + Status int `json:"status" dc:"状态:-1为全部,0为正常,1为停用"` + Name string `json:"name" dc:"名称"` +} +type MenuColumnDoRes struct { + Data []*model.UserMenuColumnRes +} + +type AddMenuColumnReq struct { + g.Meta `path:"/menu/column/add" tags:"菜单管理" method:"post" summary:"添加菜单与列表相关关联"` + ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择菜单ID"` + Name string `json:"name" description:"名称" v:"required#请输入名称"` + Code string `json:"code" description:"代表列表"` + Description string `json:"description" description:"描述"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} +type AddMenuColumnRes struct { +} + +type DetailMenuColumnReq struct { + g.Meta `path:"/menu/column/detail" tags:"菜单管理" method:"get" summary:"根据ID获取菜单列表详情"` + Id int64 `p:"id" description:"菜单列表ID" v:"required#ID不能为空"` +} +type DetailMenuColumnRes struct { + Data *model.DetailMenuColumnRes +} + +type EditMenuColumnReq struct { + g.Meta `path:"/menu/column/edit" method:"put" summary:"编辑菜单列表" tags:"菜单管理"` + Id int `json:"id" description:"" v:"required#ID不能为空"` + ParentId int `json:"parentId" description:"父ID" v:"required#请选择上级"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` + Name string `json:"name" description:"名称" v:"required#请输入名称"` + Code string `json:"code" description:"代表列表"` + Description string `json:"description" description:"描述"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} +type EditMenuColumnRes struct { +} + +type DelMenuColumnReq struct { + g.Meta `path:"/menu/column/del" method:"delete" summary:"根据ID删除菜单列表" tags:"菜单管理"` + Id int64 `p:"id" description:"菜单列表ID" v:"required#ID不能为空"` +} +type DelMenuColumnRes struct { +} + +type EditMenuColumnStatusReq struct { + g.Meta `path:"/menu/column/editStatus" method:"put" summary:"编辑菜单列表状态" tags:"菜单管理"` + Id int `json:"id" description:"" v:"required#ID不能为空"` + MenuId int `json:"menuId" description:"菜单ID" v:"required#请选择关联菜单"` + Status int `json:"status" description:"状态 0 停用 1启用"` +} + +type EditMenuColumnStatusRes struct { +} diff --git a/api/v1/system/sys_message.go b/api/v1/system/sys_message.go new file mode 100644 index 0000000..5911e71 --- /dev/null +++ b/api/v1/system/sys_message.go @@ -0,0 +1,60 @@ +package system + +import ( + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/common" + "sagooiot/internal/model" +) + +type GetMessageListReq struct { + g.Meta `path:"/message/list" method:"get" summary:"获取消息列表" tags:"消息管理"` + Types string `json:"types" description:"消息类型"` + Title string `json:"title" description:"标题"` + *common.PaginationReq +} +type GetMessageListRes struct { + Info []*model.MessageListRes + common.PaginationRes +} + +type GetUnReadMessageAllReq struct { + g.Meta `path:"/message/allUnRead" method:"get" summary:"获取所有未读消息" tags:"消息管理"` + *common.PaginationReq +} +type GetUnReadMessageAllRes struct { + Info []*model.MessageListRes + common.PaginationRes +} + +type GetUnReadMessageCountReq struct { + g.Meta `path:"/message/unReadCount" method:"get" summary:"获取所有未读消息数量" tags:"消息管理"` +} +type GetUnReadMessageCountRes struct { + Count int +} + +type DelMessageReq struct { + g.Meta `path:"/message/del" method:"delete" summary:"批量删除消息" tags:"消息管理"` + Ids []int `json:"ids" description:"ID" v:"required#ID不能为空"` +} +type DelMessageRes struct { +} + +type ClearMessageReq struct { + g.Meta `path:"/message/clear" method:"delete" summary:"一键清空消息" tags:"消息管理"` +} +type ClearMessageRes struct { +} + +type ReadMessageReq struct { + g.Meta `path:"/message/read" method:"put" summary:"阅读消息" tags:"消息管理"` + Id int `json:"id" description:"ID" v:"required#ID不能为空"` +} +type ReadMessageRes struct { +} + +type ReadMessageAllReq struct { + g.Meta `path:"/message/readAll" method:"put" summary:"全部已读" tags:"消息管理"` +} +type ReadMessageAllRes struct { +} diff --git a/api/v1/system/sys_monitor.go b/api/v1/system/sys_monitor.go index 5205cec..4f4261d 100644 --- a/api/v1/system/sys_monitor.go +++ b/api/v1/system/sys_monitor.go @@ -1,9 +1,46 @@ package system -import "github.com/gogf/gf/v2/frame/g" +import ( + "github.com/gogf/gf/v2/frame/g" +) -type MonitorSearchReq struct { - g.Meta `path:"/monitor/server" tags:"服务监控" method:"get" summary:"服务监控"` +type ListLogsReq struct { + g.Meta `path:"/monitor/listLogs" tags:"系统日志" method:"get" summary:"日志列表"` + Types string `json:"types" description:"日志类型" v:"required#types不能为空"` +} +type ListLogsRes struct { + List []LogInfo `json:"list" ` +} + +type LogInfo struct { + Name string `json:"name" description:"日志文件名" ` + Size string `json:"size" description:"日志大小" ` + ChangeAt string `json:"changeAt" description:"修改时间" ` } -type MonitorSearchRes g.Map +type LastLinesLogReq struct { + g.Meta `path:"/monitor/lastLinesLog" tags:"系统日志" method:"get" summary:"查看日志内容"` + Types string `json:"types" description:"日志类型" v:"required#types不能为空"` + Name string `json:"name" description:"日志文件名" v:"required#name不能为空"` +} + +type LastLinesLogRes struct { + List []string `json:"list"` +} + +// DeleteLogReq 删除日志 +type DeleteLogReq struct { + g.Meta `path:"/monitor/lastLinesLog/delete" method:"delete" tags:"系统日志" summary:"删除日志"` + Types string `json:"types" description:"日志类型" v:"required#types不能为空"` + Name string `json:"name" description:"日志文件名" v:"required#name不能为空"` +} +type DeleteLogRes struct{} + +type DownloadSysLogReq struct { + g.Meta `path:"/monitor/downloadLog" tags:"系统日志" method:"get" summary:"系统日志下载"` + Name string `json:"name" description:"日志文件名" v:"required#name不能为空"` + Types string `json:"types" description:"日志类型" v:"required#types不能为空"` +} +type DownloadSysLogRes struct { + g.Meta `mime:"text/html" example:"string"` +} diff --git a/api/v1/system/sys_notifications.go b/api/v1/system/sys_notifications.go index 278b825..2babde0 100644 --- a/api/v1/system/sys_notifications.go +++ b/api/v1/system/sys_notifications.go @@ -2,11 +2,11 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" ) -//获取列表api +// 获取列表api type GetNotificationsListReq struct { g.Meta `path:"/notifications/list" method:"get" summary:"获取消息列表" tags:"通知中心管理"` common.PaginationReq @@ -16,7 +16,7 @@ type GetNotificationsListRes struct { common.PaginationRes } -//获取指定ID的数据api +// 获取指定ID的数据api type GetNotificationsByIdReq struct { g.Meta `path:"/notifications/get" method:"get" summary:"获取消息列表" tags:"通知中心管理"` Id int `json:"id" description:"id" v:"required#id不能为空"` @@ -25,7 +25,7 @@ type GetNotificationsByIdRes struct { Data *model.NotificationsRes } -//添加数据api +// 添加数据api type AddNotificationsReq struct { g.Meta `path:"/notifications/add" method:"post" summary:"添加消息" tags:"通知中心管理"` Types string `json:"types" description:"类型"` @@ -37,7 +37,7 @@ type AddNotificationsReq struct { } type AddNotificationsRes struct{} -//编辑数据api +// 编辑数据api type EditNotificationsReq struct { g.Meta `path:"/notifications/edit" method:"put" summary:"编辑消息" tags:"通知中心管理"` Id string `json:"id" description:""` @@ -50,7 +50,7 @@ type EditNotificationsReq struct { } type EditNotificationsRes struct{} -//删除数据api +// 删除数据api type DeleteNotificationsReq struct { g.Meta `path:"/notifications/delete" method:"delete" summary:"删除消息" tags:"通知中心管理"` Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` diff --git a/api/v1/system/sys_oper_log.go b/api/v1/system/sys_oper_log.go index 3861062..66ce7cd 100644 --- a/api/v1/system/sys_oper_log.go +++ b/api/v1/system/sys_oper_log.go @@ -1,9 +1,9 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/system/sys_organization.go b/api/v1/system/sys_organization.go index dd5ead8..4bb5919 100644 --- a/api/v1/system/sys_organization.go +++ b/api/v1/system/sys_organization.go @@ -1,7 +1,7 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/system/sys_plugins.go b/api/v1/system/sys_plugins.go index fd8f8ed..fceba98 100644 --- a/api/v1/system/sys_plugins.go +++ b/api/v1/system/sys_plugins.go @@ -2,9 +2,9 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - - "github.com/sagoo-cloud/sagooiot/internal/model" + "github.com/gogf/gf/v2/net/ghttp" + "sagooiot/api/v1/common" + "sagooiot/internal/model" ) // GetSysPluginsListReq 获取数据列表 @@ -13,7 +13,7 @@ type GetSysPluginsListReq struct { common.PaginationReq } type GetSysPluginsListRes struct { - Data []GetSysPluginsByIdRes + Data []model.GetSysPluginsListRes common.PaginationRes } @@ -23,15 +23,15 @@ type GetSysPluginsByIdReq struct { Id int `json:"id" description:"id" v:"required#id不能为空"` } type GetSysPluginsByIdRes struct { - Version string `json:"version" description:"版本"` - Author string `json:"author" description:""` - StartTime string `json:"startTime" description:""` - Id int `json:"id" description:"ID"` - Name string `json:"name" description:"名称"` - Title string `json:"title" description:"标题"` - Intro string `json:"intro" description:"介绍"` - Status int `json:"status" description:"状态"` - Types string `json:"types" description:"插件类型"` + *model.SysPluginsRes +} + +// AddSysPluginsReq 添加插件 +type AddSysPluginsReq struct { + g.Meta `path:"/plugins/add" method:"post" summary:"添加插件" tags:"插件管理"` + File *ghttp.UploadFile `json:"file" type:"file" dc:"上传文件" v:"required#请上传文件"` +} +type AddSysPluginsRes struct { } type EditSysPluginsStatusReq struct { @@ -41,6 +41,33 @@ type EditSysPluginsStatusReq struct { } type EditSysPluginsStatusRes struct{} +type EditSysPluginsReq struct { + g.Meta `path:"/plugins/edit" method:"put" summary:"修改插件信息" tags:"插件管理"` + Id int `json:"id" description:"ID" v:"required#id不能为空"` + Types string `json:"types" description:"插件与SagooIOT的通信方式" v:"required#通信方式不能为空"` + HandleType string `json:"handleType" description:"功能类型" v:"required#功能类型不能为空"` + Name string `json:"name" description:"名称" v:"required#名称不能为空"` + Title string `json:"title" description:"标题" v:"required#标题不能为空"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author []string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args []string `json:"args" description:"插件的指令参数"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` +} +type EditSysPluginsRes struct { +} + +type DelSysPluginsStatusReq struct { + g.Meta `path:"/plugins/del" method:"delete" summary:"删除插件" tags:"插件管理"` + Ids []int `json:"ids" description:"ID"` +} +type DelSysPluginsStatusRes struct{} + type GetSysPluginsTypesAllReq struct { g.Meta `path:"/plugins/getTypesAll" method:"get" summary:"获取插件类型" tags:"插件管理"` Types string `json:"types" description:"功能类型" v:"required#插件类型不能为空 协议(protocol)或者通知(notice)"` diff --git a/api/v1/system/sys_plugins_config.go b/api/v1/system/sys_plugins_config.go index 58d79ca..0d6f85a 100644 --- a/api/v1/system/sys_plugins_config.go +++ b/api/v1/system/sys_plugins_config.go @@ -2,10 +2,10 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" + "sagooiot/api/v1/common" ) -//GetPluginsConfigListReq 获取数据列表 +// GetPluginsConfigListReq 获取数据列表 type GetPluginsConfigListReq struct { g.Meta `path:"/plugins_config/list" method:"get" summary:"获取插件配置列表" tags:"插件管理"` common.PaginationReq @@ -15,7 +15,7 @@ type GetPluginsConfigListRes struct { common.PaginationRes } -//GetPluginsConfigByIdReq 获取指定ID的数据 +// GetPluginsConfigByIdReq 获取指定ID的数据 type GetPluginsConfigByIdReq struct { g.Meta `path:"/plugins_config/get" method:"get" summary:"获取插件配置" tags:"插件管理"` Id int `json:"id" description:"id" v:"required#id不能为空"` @@ -28,7 +28,7 @@ type GetPluginsConfigByIdRes struct { Name string `json:"name" description:"插件名称"` } -//GetPluginsConfigByNameReq 获取指定类型与名称的的插件数据 +// GetPluginsConfigByNameReq 获取指定类型与名称的的插件数据 type GetPluginsConfigByNameReq struct { g.Meta `path:"/plugins_config/getbyname" method:"get" summary:"获取指定类型与名称的的插件数据" tags:"插件管理"` Type string `json:"type" description:"type" v:"required#类型不能为空"` @@ -42,7 +42,7 @@ type GetPluginsConfigByNameRes struct { Name string `json:"name" description:"插件名称"` } -//AddPluginsConfigReq 添加数据 +// AddPluginsConfigReq 添加数据 type AddPluginsConfigReq struct { g.Meta `path:"/plugins_config/add" method:"post" summary:"添加插件配置" tags:"插件管理"` Type string `json:"type" description:"插件类型"` @@ -52,7 +52,7 @@ type AddPluginsConfigReq struct { } type AddPluginsConfigRes struct{} -//SavePluginsConfigReq 直接更新配置数据 +// SavePluginsConfigReq 直接更新配置数据 type SavePluginsConfigReq struct { g.Meta `path:"/plugins_config/save" method:"post" summary:"直接更新配置数据" tags:"插件管理"` Type string `json:"type" description:"type" v:"required#插件类型不能为空"` @@ -62,7 +62,7 @@ type SavePluginsConfigReq struct { } type SavePluginsConfigRes struct{} -//EditPluginsConfigReq 编辑数据api +// EditPluginsConfigReq 编辑数据api type EditPluginsConfigReq struct { g.Meta `path:"/plugins_config/edit" method:"put" summary:"编辑插件配置" tags:"插件管理"` Value string `json:"value" description:"配置内容"` @@ -73,7 +73,7 @@ type EditPluginsConfigReq struct { } type EditPluginsConfigRes struct{} -//DeletePluginsConfigReq 删除数据 +// DeletePluginsConfigReq 删除数据 type DeletePluginsConfigReq struct { g.Meta `path:"/plugins_config/delete" method:"delete" summary:"删除插件配置" tags:"插件管理"` Ids []int `json:"ids" description:"ids" v:"required#ids不能为空"` diff --git a/api/v1/system/sys_post.go b/api/v1/system/sys_post.go index d5f6341..bdfa01a 100644 --- a/api/v1/system/sys_post.go +++ b/api/v1/system/sys_post.go @@ -1,7 +1,7 @@ package system import ( - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/frame/g" ) diff --git a/api/v1/system/sys_role.go b/api/v1/system/sys_role.go index 8d884f6..b77116e 100644 --- a/api/v1/system/sys_role.go +++ b/api/v1/system/sys_role.go @@ -2,7 +2,7 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type RoleTreeReq struct { diff --git a/api/v1/system/sys_user.go b/api/v1/system/sys_user.go index 47680e5..458bc21 100644 --- a/api/v1/system/sys_user.go +++ b/api/v1/system/sys_user.go @@ -2,8 +2,8 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" ) type UserListReq struct { diff --git a/api/v1/system/sys_user_online.go b/api/v1/system/sys_user_online.go index 79c0853..01e128d 100644 --- a/api/v1/system/sys_user_online.go +++ b/api/v1/system/sys_user_online.go @@ -2,8 +2,8 @@ package system import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/common" + "sagooiot/internal/model" ) type UserOnlineListReq struct { diff --git a/api/v1/tdengine/td_engine.go b/api/v1/tdengine/td_engine.go index 60ed4f3..d3b78d7 100644 --- a/api/v1/tdengine/td_engine.go +++ b/api/v1/tdengine/td_engine.go @@ -2,7 +2,7 @@ package tdengine import ( "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type GetTdEngineAllDbReq struct { diff --git a/api/v1/thing/overview.go b/api/v1/thing/overview.go deleted file mode 100644 index ee15216..0000000 --- a/api/v1/thing/overview.go +++ /dev/null @@ -1,14 +0,0 @@ -package thing - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/model" -) - -// 物联概览统计数据 -type ThingOverviewReq struct { - g.Meta `path:"/overview" method:"get" summary:"物联概览统计数据" tags:"数据概览"` -} -type ThingOverviewRes struct { - model.ThingOverviewOutput -} diff --git a/build.sh b/build.sh index 4b2b37b..cf22c6c 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/sh #应用的名称 -AppName=sagoo-admin +AppName=sagooiot BuildVersion=$(git describe --abbrev=0 --tags) BuildTime=$(date +%FT%T%z) @@ -66,8 +66,10 @@ copyFile() { mkdir bin mkdir bin/resource mkdir bin/resource/public + mkdir bin/resource/public/rsa cp -r ./manifest/config/. bin/config/ + cp -r ./resource/rsa/. bin/resource/rsa/ echo "${BuildVersion} $(date +%T)" } diff --git a/curl.sh b/curl.sh index b15ca32..63c61a8 100755 --- a/curl.sh +++ b/curl.sh @@ -1,103 +1,96 @@ #!/bin/bash - -WORKSPACE=$(cd $(dirname $0)/ || exit; pwd) + +WORKSPACE=$(cd "$(dirname "$0")" || exit; pwd) cd "$WORKSPACE" || exit -mkdir -p var +readonly app='sagooiot' +readonly pidfile="var/$app.pid" +readonly logfile="var/$app.log" -app=sagoo-admin -pidfile=var/$app.pid -logfile=var/$app.log +mkdir -p var -function check_pid() { - if [ -f $pidfile ];then - pid=$(cat $pidfile) +check_pid() { + if [[ -f $pidfile ]]; then + local pid=$(cat "$pidfile") if [[ -n $pid ]]; then - running=$(ps -p "$pid"|grep -c -v "PID TTY") + local running=$(ps -p "$pid" | grep -c -v "PID TTY") return "$running" fi fi return 0 } +start() { + check_pid + local running=$? + if [[ $running -gt 0 ]]; then + printf "%s now is running already, pid: %s\n" "$app" "$(cat "$pidfile")" + return 1 + fi - function start(){ - check_pid - running=$? - if [ $running -gt 0 ]; then - echo -n "$app now is running already,pid=" - cat $pidfile - return - fi - - nohup ./$app &>> $logfile & + nohup "./$app" >> "$logfile" 2>&1 & sleep 1 running=$(ps -p $! | grep -c -v "PID TTY") - if [ "$running" -gt 0 ];then - echo $! > $pidfile - echo "$app started..., pid=$!" + if [[ $running -gt 0 ]]; then + echo $! > "$pidfile" + printf "%s started... pid: %s\n" "$app" "$!" else - echo "$app failed to start" + printf "%s failed to start.\n" "$app" return 1 fi +} - } - -function stop() { +stop() { check_pid - running=$? - if [ $running -gt 0 ];then - pid=$(cat $pidfile) + local running=$? + if [[ $running -gt 0 ]]; then + local pid=$(cat "$pidfile") kill "$pid" - rm -f $pidfile - echo "$app stoped" + rm -f "$pidfile" + printf "%s stopped.\n" "$app" else - echo "$app already stoped" + printf "%s is not running.\n" "$app" fi } -function restart() { +restart() { stop sleep 1 start } -function status() { +status() { check_pid - running=$? - if [ $running -gt 0 ];then - echo "started" + local running=$? + if [[ $running -gt 0 ]]; then + printf "%s is started.\n" "$app" else - echo "stoped" + printf "%s is stopped.\n" "$app" fi } -function tailf() { +tailf() { tail -f var/* } -function help() { - echo "$0 pid|start|stop|restart|status|tail" +print_help() { + printf "Usage: %s {start|stop|restart|status|tail|pid}.\n" "$0" } -function pid() { - cat $pidfile +print_pid() { + cat "$pidfile" } -if [ "$1" == "" ]; then - help -elif [ "$1" == "stop" ];then - stop -elif [ "$1" == "start" ];then - start -elif [ "$1" == "restart" ];then - restart -elif [ "$1" == "status" ];then - status -elif [ "$1" == "tail" ];then - tailf -elif [ "$1" == "pid" ];then - pid -else - help -fi +main() { + case "$1" in + "start") start ;; + "stop") stop ;; + "restart") restart ;; + "status") status ;; + "tail") tailf ;; + "pid") print_pid ;; + *) print_help ;; + esac +} + +main "$@" \ No newline at end of file diff --git a/extend/extend.go b/extend/extend.go deleted file mode 100644 index b4ae317..0000000 --- a/extend/extend.go +++ /dev/null @@ -1,151 +0,0 @@ -package extend - -import ( - "context" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginType" - "github.com/sagoo-cloud/sagooiot/extend/model" - "github.com/sagoo-cloud/sagooiot/extend/module" - "sync" -) - -type SysPlugin struct { - pluginManager *Manager -} - -var ins *SysPlugin -var once sync.Once - -// GetNoticePlugin 构造方法 -func GetNoticePlugin() *SysPlugin { - once.Do(func() { - ins = &SysPlugin{} - pm, err := pluginInit(PluginType.Notice) - if err != nil { - g.Log().Error(context.TODO(), err.Error()) - } - ins.pluginManager = pm - }) - - return ins -} - -// GetProtocolPlugin 构造方法 -func GetProtocolPlugin() *SysPlugin { - once.Do(func() { - ins = &SysPlugin{} - pm, err := pluginInit(PluginType.Protocol) - if err != nil { - g.Log().Error(context.TODO(), err.Error()) - } - ins.pluginManager = pm - }) - - return ins -} - -// 初始化处理协议插件 -func pluginInit(sysPluginType string) (pm *Manager, err error) { - // 静态目录设置 - pluginsPath := g.Cfg().MustGet(context.TODO(), "system.pluginsPath").String() - //pluginsPath := "../plugins/built" - switch sysPluginType { - case PluginType.Notice: - pm = NewManager(sysPluginType, PluginType.Notice+"-*", pluginsPath, &module.NoticePlugin{}) - defer pm.Dispose() - - break - case PluginType.Protocol: - pm = NewManager(sysPluginType, PluginType.Protocol+"-*", pluginsPath, &module.ProtocolPlugin{}) - defer pm.Dispose() - - break - default: - err = gerror.New("无效的插件类型") - return - } - - //defer ProtocolPlugin.Dispose() - //初始化管理器 - err = pm.Init() - //重启所有插件 - err = pm.Launch() - - return -} - -// Deprecated: GetProtocolPlugin 这个方法已经废弃,不建议使用。请使用GetProtocolByName -func (pm *SysPlugin) GetProtocolPlugin(protocolName string) (obj module.Protocol, err error) { - //获取插件 - p, err := pm.pluginManager.GetInterface(protocolName) - if err != nil { - return - } - obj = p.(module.Protocol) - return -} - -// GetProtocolByName 获取指定协议名称的插件 -func (pm *SysPlugin) GetProtocolByName(protocolName string) (obj module.Protocol, err error) { - //获取插件 - p, err := pm.pluginManager.GetInterface(protocolName) - if err != nil { - return - } - obj = p.(module.Protocol) - return -} - -// GetNoticeByName 获取指定通知名称的插件 -func (pm *SysPlugin) GetNoticeByName(noticeName string) (obj module.Notice, err error) { - //获取插件 - p, err := pm.pluginManager.GetInterface(noticeName) - if err != nil { - g.Log().Error(context.Background(), err.Error()) - return - } - obj = p.(module.Notice) - return -} - -// GetProtocolUnpackData 通过协议解析插件处理后,获取解析数据。protocolType 为协议名称 -// todo 需要标记数据协议子类型 -func (pm *SysPlugin) GetProtocolUnpackData(protocolType string, data []byte) (res model.JsonRes, err error) { - //获取插件 - p, err := pm.pluginManager.GetInterface(protocolType) - if err != nil { - return - } - - var rd = model.DataReq{} - rd.Data = data - resData := p.(module.Protocol).Decode(rd) - return resData, err -} - -// NoticeSend 通过插件发送通知信息。noticeName 为通知插件名称;msg为通知内容 -func (pm *SysPlugin) NoticeSend(noticeName string, msg model.NoticeInfoData) (res string, err error) { - //获取插件 - p, err := pm.pluginManager.GetInterface(noticeName) - if err != nil { - return - } - - var nd = new(model.NoticeData) - nd.Msg = msg - cfgData, err := getPluginsConfigData(PluginType.Notice, noticeName) - if err != nil { - return - } - nd.Config = cfgData - ndJson := gjson.New(nd) - //转为byte - byteData := ndJson.MustToJson() - - sendRes := p.(module.Notice).Send(byteData) - res, err = gjson.New(sendRes).ToJsonString() - g.Log().Debug(context.TODO(), "通知发送结果:", res) - return -} diff --git a/extend/manager.go b/extend/manager.go deleted file mode 100644 index 4e27eb7..0000000 --- a/extend/manager.go +++ /dev/null @@ -1,159 +0,0 @@ -package extend - -import ( - "context" - "errors" - "fmt" - "github.com/gogf/gf/v2/encoding/gyaml" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/extend/module" - "os/exec" - "path/filepath" - "strings" - "sync" - - "github.com/hashicorp/go-plugin" -) - -type PluginInfo struct { - ID string - Path string - Client *plugin.Client -} - -func NewManager(ptype, glob, dir string, pluginImpl plugin.Plugin) *Manager { - - manager := &Manager{ - Type: ptype, - Glob: glob, - Path: dir, - Plugins: map[string]*PluginInfo{}, - pluginImpl: pluginImpl, - } - - return manager -} - -// Manager 为不同类型的插件,管理的生命周期 -type Manager struct { - Type string // 管理器处理的插件类型的id - Glob string // 全局的插件文件名 - Path string // 插件路径 - Plugins map[string]*PluginInfo // 插件信息列表 - initialized bool // 是否初始化 - pluginImpl plugin.Plugin // 插件实现虚拟接口 -} - -func (m *Manager) Init() error { - - //发现插件绝对路径 - plugins, err := plugin.Discover(m.Glob, m.Path) - if err != nil { - return err - } - - //获取所有插件信息 - for _, p := range plugins { - _, file := filepath.Split(p) - globAsterix := strings.LastIndex(m.Glob, "*") - trim := m.Glob[0:globAsterix] - id := strings.TrimPrefix(file, trim) - - //添加到插件信息 - m.Plugins[id] = &PluginInfo{ - ID: id, - Path: p, - } - } - - m.initialized = true - - return nil -} - -func (m *Manager) Launch() error { - - for id, info := range m.Plugins { - - fmt.Printf("注册插件 type=%s, id=%s, impl=%s \n", m.Type, id, info.Path) - // 创建新的客户端 - // 两种方式选其一 - // 以exec.Command方式启动插件进程,并创建宿主机进程和插件进程的连接 - // 或者使用Reattach连接到现有进程,需提供Reattach信息 - client := plugin.NewClient(&plugin.ClientConfig{ - HandshakeConfig: module.HandshakeConfig, - Plugins: m.pluginMap(id), - //创建新进程,或使用Reattach连接到现有进程中 - Cmd: exec.Command(info.Path), - }) - - if _, ok := m.Plugins[id]; !ok { - // 如果没有找到,忽略? - continue - } - pinfo := m.Plugins[id] - pinfo.Client = client - - } - - return nil -} - -func (m *Manager) Dispose() { - var wg sync.WaitGroup - for _, pinfo := range m.Plugins { - wg.Add(1) - - go func(client *plugin.Client) { - // 关闭client,释放相关资源,终止插件子程序的运行 - client.Kill() - wg.Done() - }(pinfo.Client) - } - - wg.Wait() - -} - -func (m *Manager) GetInterface(id string) (interface{}, error) { - - if _, ok := m.Plugins[id]; !ok { - return nil, errors.New("在注册的插件中找不到插件ID! " + id) - } - - //获取注册插件客户端 plugin.Client - client := m.Plugins[id].Client - - // 返回协议客户端,如rpc客户端或grpc客户端,用于后续通信 - rpcClient, err := client.Client() - if err != nil { - return nil, err - } - - // 根据指定插件名称分配新实例 - raw, err := rpcClient.Dispense(id) - if err != nil { - return nil, err - } - - return raw, nil -} - -// pluginMap //插件名称到插件对象的映射关系 -func (m *Manager) pluginMap(id string) map[string]plugin.Plugin { - pMap := map[string]plugin.Plugin{} - - pMap[id] = m.pluginImpl - - return pMap -} - -func getPluginsConfigData(pluginType, pluginName string) (res map[interface{}]interface{}, err error) { - key := "plugins" + pluginType + pluginName - fmt.Println(key) - pcgData, err := g.Redis().Do(context.TODO(), "GET", key) - - err = gyaml.DecodeTo([]byte(pcgData.String()), &res) - - return -} diff --git a/extend/model/notice.go b/extend/model/notice.go deleted file mode 100644 index a4dfc08..0000000 --- a/extend/model/notice.go +++ /dev/null @@ -1,26 +0,0 @@ -package model - -type NoticeSendObject struct { - Name string `json:"name"` - Value string `json:"value"` -} - -type NoticeInfoData struct { - ConfigId string `orm:"config_id" json:"config_id"` // - ComeFrom string `orm:"come_from" json:"come_from"` // - Method string `orm:"method" json:"method"` // - MethodCron string `orm:"method_cron" json:"method_cron"` // - MethodNum int `orm:"method_num" json:"method_num"` // - MsgTitle string `orm:"msg_title" json:"msg_title"` // - MsgBody string `orm:"msg_body" json:"msg_body"` // - MsgUrl string `orm:"msg_url" json:"msg_url"` // - UserIds string `orm:"user_ids" json:"user_ids"` // - PartyIds string `orm:"party_ids" json:"party_ids"` // - Totag string `orm:"totag" json:"totag"` // -} - -type NoticeData struct { - Config map[interface{}]interface{} - SendParam map[string]interface{} - Msg NoticeInfoData -} diff --git a/extend/model/sagoomqtt.go b/extend/model/sagoomqtt.go deleted file mode 100644 index 36bd080..0000000 --- a/extend/model/sagoomqtt.go +++ /dev/null @@ -1,30 +0,0 @@ -package model - -import "encoding/gob" - -func init() { - gob.Register(SagooMqttModel{}) -} - -// SagooMqttModel 主结构 -type ( - SagooMqttModel struct { - Id string `json:"id"` - Version string `json:"version"` - Sys SysInfo `json:"sys"` - Params map[string]Param `json:"params"` - Method string `json:"method"` - ModelFuncName string `json:"model_func_name"` - ModelFuncIdentify string `json:"model_func_identify"` - } - - SysInfo struct { - Ack int `json:"ack"` - } - - // Param 属性 - Param struct { - Value interface{} `json:"value"` - Time int64 `json:"time"` - } -) diff --git a/go.mod b/go.mod index 634c531..415c35e 100644 --- a/go.mod +++ b/go.mod @@ -1,90 +1,108 @@ -module github.com/sagoo-cloud/sagooiot +module sagooiot -go 1.18 +go 1.21 require ( - github.com/eclipse/paho.mqtt.golang v1.4.1 - github.com/fastwego/wxwork v1.0.0-beta.8 - github.com/go-co-op/gocron v1.17.0 - github.com/go-gota/gota v0.12.0 - github.com/gogf/gf/contrib/drivers/mysql/v2 v2.2.0 - github.com/gogf/gf/v2 v2.2.0 - github.com/gorilla/websocket v1.5.0 - github.com/hashicorp/go-plugin v1.4.10 - github.com/mojocn/base64Captcha v1.3.5 - github.com/mssola/user_agent v0.5.3 - github.com/robfig/cron/v3 v3.0.1 - github.com/russross/blackfriday/v2 v2.1.0 - github.com/shirou/gopsutil/v3 v3.22.9 - github.com/taosdata/driver-go/v3 v3.0.0 + github.com/Knetic/govaluate v3.0.0+incompatible + github.com/arl/statsviz v0.6.0 + github.com/eclipse/paho.mqtt.golang v1.4.3 + github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.1 + github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.1 + github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.1 + github.com/gogf/gf/v2 v2.6.1 + github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/golang-module/carbon/v2 v2.2.8 + github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 + github.com/gorilla/websocket v1.5.1 + github.com/hashicorp/go-plugin v1.6.0 + github.com/hibiken/asynq v0.24.1 + github.com/hpcloud/tail v1.0.0 + github.com/minio/minio-go/v7 v7.0.66 + github.com/mojocn/base64Captcha v1.3.6 + github.com/mssola/useragent v1.0.0 + github.com/pkg/errors v0.9.1 + github.com/redis/go-redis/v9 v9.3.1 + github.com/robertkrimen/otto v0.3.0 + github.com/shirou/gopsutil/v3 v3.23.11 + github.com/taosdata/driver-go/v3 v3.5.1 github.com/tealeg/xlsx v1.0.5 - github.com/tencentyun/cos-go-sdk-v5 v0.7.34 - github.com/tiger1103/gfast-cache v0.0.7 - github.com/tiger1103/gfast-token v0.1.1 - github.com/xinjiayu/sse v0.0.0-20221022122111-e702197c579c - gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df + github.com/tencentyun/cos-go-sdk-v5 v0.7.45 + github.com/xinjiayu/sse v1.0.1 + github.com/xuri/excelize/v2 v2.8.0 + go.opentelemetry.io/otel v1.18.0 ) require ( - github.com/denisenkom/go-mssqldb v0.11.0 // indirect - github.com/faabiosr/cachego v0.15.0 // indirect - github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect - github.com/hashicorp/go-hclog v0.14.1 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect - gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect -) - -require ( - github.com/BurntSushi/toml v1.1.0 // indirect - github.com/Knetic/govaluate v3.0.0+incompatible - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 // indirect - github.com/clbanning/mxj/v2 v2.5.5 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/clbanning/mxj v1.8.4 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/fatih/color v1.13.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-redis/redis/v8 v8.11.5 // indirect - github.com/go-sql-driver/mysql v1.6.0 // indirect - github.com/gogf/gf/contrib/drivers/mssql/v2 v2.2.1 - github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.0.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/grokify/html-strip-tags-go v0.0.1 // indirect - github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/hashicorp/go-hclog v0.14.1 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/minio/md5-simd v1.1.2 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/go-testing-interface v1.0.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/mozillazg/go-httpheader v0.2.1 // indirect github.com/oklog/run v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/rivo/uniseg v0.4.2 // indirect - github.com/tklauser/go-sysconf v0.3.10 // indirect - github.com/tklauser/numcpus v0.4.0 // indirect - github.com/yusufpapurcu/wmi v1.2.2 // indirect - go.opentelemetry.io/otel v1.7.0 // indirect - go.opentelemetry.io/otel/sdk v1.7.0 // indirect - go.opentelemetry.io/otel/trace v1.7.0 // indirect - golang.org/x/image v0.0.0-20210216034530-4410531fe030 // indirect - golang.org/x/net v0.4.0 // indirect - golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 // indirect - golang.org/x/sys v0.3.0 // indirect - golang.org/x/text v0.5.0 // indirect - gonum.org/v1/gonum v0.9.1 // indirect - google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect - google.golang.org/grpc v1.51.0 // indirect - google.golang.org/protobuf v1.28.1 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/richardlehane/mscfb v1.0.4 // indirect + github.com/richardlehane/msoleps v1.0.3 // indirect + github.com/rivo/uniseg v0.4.4 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/rs/xid v1.5.0 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca // indirect + github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.opentelemetry.io/otel/exporters/jaeger v1.14.0 // indirect + go.opentelemetry.io/otel/metric v1.18.0 // indirect + go.opentelemetry.io/otel/sdk v1.18.0 // indirect + go.opentelemetry.io/otel/trace v1.18.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/image v0.13.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.16.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect + google.golang.org/grpc v1.56.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/fsnotify.v1 v1.4.7 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/sourcemap.v1 v1.0.5 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 554c57a..12be006 100644 --- a/go.sum +++ b/go.sum @@ -1,397 +1,344 @@ -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= -github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE= +github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s= +github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= -github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E= -github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL/7ND8HI= -github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/eclipse/paho.mqtt.golang v1.4.1 h1:tUSpviiL5G3P9SZZJPC4ZULZJsxQKXxfENpMvdbAXAI= -github.com/eclipse/paho.mqtt.golang v1.4.1/go.mod h1:JGt0RsEwEX+Xa/agj90YJ9d9DH2b7upDZMK9HRbFvCA= -github.com/faabiosr/cachego v0.15.0 h1:IqcDhvzMbL4a1c9Dek88DIWJYQ5HG//L0PKCReneOA4= -github.com/faabiosr/cachego v0.15.0/go.mod h1:L2EomlU3/rUWjzFavY9Fwm8B4zZmX2X6u8kTMkETrwI= -github.com/fastwego/wxwork v1.0.0-beta.8 h1:TJaAMwby7s3oKTPDawokYKAdSbnUUHJjMBsTk7eFEU0= -github.com/fastwego/wxwork v1.0.0-beta.8/go.mod h1:BzD2YB7T/1dPzKyavIcqJxn+n9eWFpUTwhgriabBWi8= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= +github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/go-co-op/gocron v1.17.0 h1:IixLXsti+Qo0wMvmn6Kmjp2csk2ykpkcL+EmHmST18w= -github.com/go-co-op/gocron v1.17.0/go.mod h1:IpDBSaJOVfFw7hXZuTag3SCSkqazXBBUkbQ1m1aesBs= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gota/gota v0.12.0 h1:T5BDg1hTf5fZ/CO+T/N0E+DDqUhvoKBl+UVckgcAAQg= -github.com/go-gota/gota v0.12.0/go.mod h1:UT+NsWpZC/FhaOyWb9Hui0jXg0Iq8e/YugZHTbyW/34= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gogf/gf/contrib/drivers/mssql/v2 v2.2.1 h1:AhLJkS0Py0wx095bLJkGWwgTI7dBkzj2oFx0f35M31U= -github.com/gogf/gf/contrib/drivers/mssql/v2 v2.2.1/go.mod h1:0e6a31p3gl7fXOT48EzpkDcUHLXYUyIEntczX/wIx5g= -github.com/gogf/gf/contrib/drivers/mysql/v2 v2.2.0 h1:uRF+lXyUPBNxvhMGdUWxxfEtLM7mNbrb+8AqQBAXgcQ= -github.com/gogf/gf/contrib/drivers/mysql/v2 v2.2.0/go.mod h1:z+/0qiOwMroAnj5ESuobTv0l5P83rf+XR3r6Fj8WJyk= -github.com/gogf/gf/v2 v2.0.0-rc.0.20220117131058-9345eb5e946f/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM= -github.com/gogf/gf/v2 v2.0.0/go.mod h1:apktt6TleWtCIwpz63vBqUnw8MX8gWKoZyxgDpXFtgM= -github.com/gogf/gf/v2 v2.1.0-rc4/go.mod h1:thvkyb43RWUu/m05sRm4CbH9r7t7/FrW2M56L9Ystwk= -github.com/gogf/gf/v2 v2.2.0 h1:J1b+ORVr9GQyuvb7PlQq07IfU2Qe89zN2gJXXu8nBb0= -github.com/gogf/gf/v2 v2.2.0/go.mod h1:thvkyb43RWUu/m05sRm4CbH9r7t7/FrW2M56L9Ystwk= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.1 h1:5VW1vlaFNSHHhMliRkGTcDshMeA52Il8T+gffJJaVMc= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.6.1/go.mod h1:jxCa1WV/W+q0F4ILebakUsqRrl7iL3qvP+Uci0eXAew= +github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.1 h1:5NWx7rZa8CbPNw1vbLzIXQFEMbKvoJVQM0GyReBRvJ8= +github.com/gogf/gf/contrib/nosql/redis/v2 v2.6.1/go.mod h1:iy1Dwp5xWfGfuWixCgGQ06ZX6lp+d9onbmSWWzi111A= +github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.1 h1:d3/8lWFWmaQ/8mzJ5GxyRpO4racPpZ3yZ8kCuejhhiY= +github.com/gogf/gf/contrib/trace/jaeger/v2 v2.6.1/go.mod h1:O0nzQLfNJtRApGHJluraTy41jc3LIvTsSkR8WAHb4f0= +github.com/gogf/gf/v2 v2.6.1 h1:n/cfXM506WjhPa6Z1CEDuHNM1XZ7C8JzSDPn2AfuxgQ= +github.com/gogf/gf/v2 v2.6.1/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-module/carbon/v2 v2.2.8 h1:a1VxHHKAR7fc1ho7sYXhS1s5S4x7+oqAf2EY5p8C46A= +github.com/golang-module/carbon/v2 v2.2.8/go.mod h1:XDALX7KgqmHk95xyLeaqX9/LJGbfLATyruTziq68SZ8= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75 h1:f0n1xnMSmBLzVfsMMvriDyA75NB/oBgILX2GcHXIQzY= +github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0= github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-plugin v1.4.10 h1:xUbmA4jC6Dq163/fWcp8P3JuHilrHHMLNRxzGQJ9hNk= -github.com/hashicorp/go-plugin v1.4.10/go.mod h1:6/1TEzT0eQznvI/gV2CM29DLSkAK/e58mUWKVsPaph0= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= +github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hibiken/asynq v0.24.1 h1:+5iIEAyA9K/lcSPvx3qoPtsKJeKI5u9aOIvUmSsazEw= +github.com/hibiken/asynq v0.24.1/go.mod h1:u5qVeSbrnfT+vtG5Mq8ZPzQu/BmCKMHvTGb91uy9Tts= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/iancoleman/strcase v0.0.0-20191112232945-16388991a334/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a h1:N9zuLhTvBSRt0gWSiJswwQ2HqDmtX/ZCDJURnKUt1Ik= +github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= +github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= +github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= +github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0= -github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/mojocn/base64Captcha v1.3.6 h1:gZEKu1nsKpttuIAQgWHO+4Mhhls8cAKyiV2Ew03H+Tw= +github.com/mojocn/base64Captcha v1.3.6/go.mod h1:i5CtHvm+oMbj1UzEPXaA8IH/xHFZ3DGY3Wh3dBpZ28E= github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ= github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60= -github.com/mssola/user_agent v0.5.3 h1:lBRPML9mdFuIZgI2cmlQ+atbpJdLdeVl2IDodjBR578= -github.com/mssola/user_agent v0.5.3/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/mssola/useragent v1.0.0 h1:WRlDpXyxHDNfvZaPEut5Biveq86Ze4o4EMffyMxmH5o= +github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/redis/go-redis/v9 v9.0.3/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds= +github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= +github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= +github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= -github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= +github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/robertkrimen/otto v0.3.0 h1:5RI+8860NSxvXywDY9ddF5HcPw0puRsd8EgbXV0oqRE= +github.com/robertkrimen/otto v0.3.0/go.mod h1:uW9yN1CYflmUQYvAMS0m+ZiNo3dMzRUDQJX0jWbzgxw= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/shirou/gopsutil/v3 v3.22.9 h1:yibtJhIVEMcdw+tCTbOPiF1VcsuDeTE4utJ8Dm4c5eA= -github.com/shirou/gopsutil/v3 v3.22.9/go.mod h1:bBYl1kjgEJpWpxeHmLI+dVHWtyAwfcmSBLDsp2TNT8A= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= +github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/taosdata/driver-go/v3 v3.0.0 h1:hJScQiBB+ks671kz7r+VYo1J8aF8MDCQzA9uYeby64U= -github.com/taosdata/driver-go/v3 v3.0.0/go.mod h1:lT4lpI3wo3hXRwP3nzm7xDs/YgYbw5YU58XingVlfsY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/taosdata/driver-go/v3 v3.5.1 h1:ln8gLJ6HR6gHU6dodmOa9utUjPUpAcdIplh6arFO26Q= +github.com/taosdata/driver-go/v3 v3.5.1/go.mod h1:H2vo/At+rOPY1aMzUV9P49SVX7NlXb3LAbKw+MCLrmU= github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.194/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.194/go.mod h1:yrBKWhChnDqNz1xuXdSbWXG56XawEq0G5j1lg4VwBD4= -github.com/tencentyun/cos-go-sdk-v5 v0.7.34 h1:xm+Pg+6m486y4eugRI7/E4WasbVmpY1hp9QBSRErgp8= -github.com/tencentyun/cos-go-sdk-v5 v0.7.34/go.mod h1:4dCEtLHGh8QPxHEkgq+nFaky7yZxQuYwgSJM87icDaw= -github.com/tiger1103/gfast-cache v0.0.7 h1:YRuSFxFdvNlIsHAndS7XjYRLd4tmXGWhvHt9rK0LVT0= -github.com/tiger1103/gfast-cache v0.0.7/go.mod h1:s6cRWyr87wz6IJNGKRV6Ahq9hcuVz8h2PAtGrO66JO8= -github.com/tiger1103/gfast-token v0.1.1 h1:NKf/Zd1SWWimSmiJsT/fSzINTlN8PR+x0Hns7ejqF0E= -github.com/tiger1103/gfast-token v0.1.1/go.mod h1:O0o947d4lwJwSHdwgsRc0YsQ/MLdN7/rn2nR9aeGGow= -github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw= -github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= -github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o= -github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= -github.com/xinjiayu/sse v0.0.0-20221022122111-e702197c579c h1:ZYV7ubpsRHKje5z9Uy9SIZaSHL3p8VF33gxwtjlk8QU= -github.com/xinjiayu/sse v0.0.0-20221022122111-e702197c579c/go.mod h1:+PHaZosXaDmfak+L6jBbo8vPdZ4JB6BvCjrNVwFml6Q= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= -github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.opentelemetry.io/otel v1.0.0/go.mod h1:AjRVh9A5/5DE7S+mZtTR6t8vpKKryam+0lREnfmS4cg= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/sdk v1.0.0/go.mod h1:PCrDHlSy5x1kjezSdL37PhbFUMjrsLRshJ2zCzeXwbM= -go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/trace v1.0.0/go.mod h1:PXTWqayeFUlJV1YDNhsJYB184+IvAH814St6o6ajzIs= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= +github.com/tencentyun/cos-go-sdk-v5 v0.7.45 h1:5/ZGOv846tP6+2X7w//8QjLgH2KcUK+HciFbfjWquFU= +github.com/tencentyun/cos-go-sdk-v5 v0.7.45/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/xinjiayu/sse v1.0.1 h1:cyFNH6ipVGUtAA6MApBOiuWRWHYAZZaQ+8YpoV49DB0= +github.com/xinjiayu/sse v1.0.1/go.mod h1:+PHaZosXaDmfak+L6jBbo8vPdZ4JB6BvCjrNVwFml6Q= +github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca h1:uvPMDVyP7PXMMioYdyPH+0O+Ta/UO1WFfNYMO3Wz0eg= +github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.8.0 h1:Vd4Qy809fupgp1v7X+nCS/MioeQmYVVzi495UCTqB7U= +github.com/xuri/excelize/v2 v2.8.0/go.mod h1:6iA2edBTKxKbZAa7X5bDhcCg51xdOn1Ar5sfoXRGrQg= +github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a h1:Mw2VNrNNNjDtw68VsEj2+st+oCSn4Uz7vZw6TbhcV1o= +github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= +go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= +go.opentelemetry.io/otel/exporters/jaeger v1.14.0 h1:CjbUNd4iN2hHmWekmOqZ+zSCU+dzZppG8XsV+A3oc8Q= +go.opentelemetry.io/otel/exporters/jaeger v1.14.0/go.mod h1:4Ay9kk5vELRrbg5z4cpP9EtmQRFap2Wb0woPG4lujZA= +go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= +go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= +go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY= +go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M= +go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= +go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3 h1:n9HxLrNxWWtEb1cA950nuEEj3QnKbtsCJ6KjcgisNUs= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030 h1:lP9pYkih3DUSC641giIXa2XqfTIbbbRr0w2EOTA7wHA= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= -golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.1 h1:HCWmqqNoELL0RAQeKBXWtkp04mGk8koafcB4He6+uhc= -gonum.org/v1/gonum v0.9.1/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U= -google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= +google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= -gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= -gopkg.in/bsm/ratelimit.v1 v1.0.0-20160220154919-db14e161995a/go.mod h1:KF9sEfUPAXdG8Oev9e99iLGnl2uJMjc5B+4y3O7x610= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= -gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= -gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/redis.v4 v4.2.4/go.mod h1:8KREHdypkCEojGKQcjMqAODMICIVwZAONWq8RowTITA= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI= +gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/hack/.gitkeep b/hack/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/hack/config.example.yaml b/hack/config.example.yaml deleted file mode 100644 index fe8768b..0000000 --- a/hack/config.example.yaml +++ /dev/null @@ -1,19 +0,0 @@ - -# 工具相关配置 -gfcli: - # 工具编译配置 - build: - name: "focus" - arch: "amd64" - system: "linux,darwin,windows" - mod: "" - cgo: 0 - - # dao生成 - gen: - dao: - - link: "mysql:root:DbyTYGu3s4WuAF4TTq7@tcp(127.0.0.1:3306)/sagoo_iot_open?loc=Local&parseTime=true" - tables: "network_server" - removePrefix: "gf_" - descriptionTag: true - noModelComment: true diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 38677ca..28334b1 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -2,19 +2,17 @@ package cmd import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/cmd/router" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/internal/task" - "github.com/sagoo-cloud/sagooiot/network" - "github.com/sagoo-cloud/sagooiot/utility/notifier" + "fmt" + "os" + "os/signal" + "sagooiot/internal/sse" + "syscall" + "time" + + "sagooiot/internal/logic/tdengine" "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/net/goai" "github.com/gogf/gf/v2/os/gcmd" - "github.com/gogf/gf/v2/util/gmode" ) var ( @@ -23,104 +21,47 @@ var ( Usage: "main", Brief: "start sagoo-iot server", Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { - var ( - s = g.Server() - ) - - // 静态目录设置 - uploadPath := g.Cfg().MustGet(ctx, "upload.path").String() - if uploadPath == "" { - g.Log().Fatal(ctx, "文件上传配置路径不能为空") - } - //s.AddStaticPath("/upload", uploadPath) - - // HOOK, 开发阶段禁止浏览器缓存,方便调试 - if gmode.IsDevelop() { - s.BindHookHandler("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) { - r.Response.Header().Set("Cache-Control", "no-store") - }) - } - - //操作日志 - s.BindHookHandler("/*", ghttp.HookAfterOutput, func(r *ghttp.Request) { - service.Middleware().OperationLog(r) - }) - - //sse 实时数据推送 - s.Group("/subscribe", func(group *ghttp.RouterGroup) { - group.GET("/sysenv", notifier.SysenvMessageEvent) - }) - - s.Group("/api/v1", func(group *ghttp.RouterGroup) { - - group.Middleware( - service.Middleware().Ctx, - service.Middleware().ResponseHandler, - service.Middleware().MiddlewareCORS, - ) - - router.System(ctx, group) //系统默认功能的路由 - router.Business(ctx, group) //业务专属功能的路由 - - }) - - //初始化系统权限缓存 - if err := service.SysAuthorize().InitAuthorize(ctx); err != nil { - g.Log().Fatal(ctx, "系统权限缓存初始化失败:", err) + var signalChannel = make(chan os.Signal, 1) + + enablePProf := g.Cfg().MustGet(context.Background(), "system.enablePProf").Bool() + if enablePProf { + pprofPort := g.Cfg().MustGet(context.Background(), "system.pprofPort").String() + if pprofPort == "" { + pprofPort = "58089" + } + RunSystemAnalysis(signalChannel, pprofPort) // 运行系统分析 } - // 初始化插件配置数据 - if err := service.SystemPluginsConfig().UpdateAllPluginsConfigCache(); err != nil { - g.Log().Error(ctx, "初始化插件配置数据失败:", err) + deferFuncListIotCore, err := InitSystemDeferFunc(ctx) + defer func() { + for _, f := range deferFuncListIotCore { + if f == nil { + continue + } + if deferErr := f(ctx); deferErr != nil { + fmt.Printf("defer func error: %s\n", deferErr.Error()) + } + } + }() + + err = InitSystem(ctx, InitFuncNoDeferListForIotCore) + if err != nil { + fmt.Printf("defer func error: %s\n", err.Error()) } - // TDengine 初始化 - if err := service.TSLTable().CreateDatabase(ctx); err != nil { - g.Log().Fatal(ctx, "TDengine 数据库创建失败:", err) - } - if err := service.TdLogTable().CreateStable(ctx); err != nil { - g.Log().Fatal(ctx, "TDengine 日志超级表创建失败:", err) - } - if err := mqtt.InitSystemMqtt(); err != nil { - g.Log().Errorf(ctx, "MQTT 初始化mqtt客户端失败,失败原因:%+#v", err) - } - defer mqtt.Close() - // 启动失败的话请注释掉 - if err := network.ReloadNetwork(context.Background()); err != nil { - g.Log().Errorf(ctx, "载入网络错误,错误原因:%+#v", err) + err = InitSystem(ctx, InitFuncNoDeferListWebAdmin) + if err != nil { + fmt.Printf("defer func error: %s\n", err.Error()) } - //初始化任务 - task.StartInit() - - // 自定义丰富文档 - enhanceOpenAPIDoc(s) - // 启动Http Server - s.Run() + sse.Init() // 启动SSE推送 + RunServer(ctx, signalChannel) + signal.Notify(signalChannel, os.Interrupt, os.Kill, syscall.SIGTERM) + fmt.Println("收到关闭服务信号:", <-signalChannel) + time.Sleep(time.Second * 3) + tdengine.Close() + fmt.Println("成功关闭服务器") return }, } ) - -func enhanceOpenAPIDoc(s *ghttp.Server) { - openapi := s.GetOpenApi() - openapi.Config.CommonResponse = ghttp.DefaultHandlerResponse{} - openapi.Config.CommonResponseDataField = `Data` - - // API description. - openapi.Info.Title = `sagooAdmin Project` - openapi.Info.Description = `` - - // Sort the tags in custom sequence. - openapi.Tags = &goai.Tags{ - {Name: consts.OpenAPITagNameLogin}, - {Name: consts.OpenAPITagNameOrganization}, - {Name: consts.OpenAPITagNameDept}, - {Name: consts.OpenAPITagNamePost}, - {Name: consts.OpenAPITagNameRole}, - {Name: consts.OpenAPITagNameUser}, - {Name: consts.OpenAPITagNameMenu}, - {Name: consts.OpenAPITagNameApi}, - {Name: consts.OpenAPITagNameAuthorize}, - } -} diff --git a/internal/cmd/doc.go b/internal/cmd/doc.go new file mode 100644 index 0000000..cc4d018 --- /dev/null +++ b/internal/cmd/doc.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/goai" + "sagooiot/internal/consts" +) + +func enhanceOpenAPIDoc(s *ghttp.Server) { + openapi := s.GetOpenApi() + openapi.Config.CommonResponse = ghttp.DefaultHandlerResponse{} + openapi.Config.CommonResponseDataField = `Data` + + // API description. + openapi.Info.Title = `sagooAdmin Project` + openapi.Info.Description = `` + + // Sort the tags in custom sequence. + openapi.Tags = &goai.Tags{ + {Name: consts.OpenAPITagNameLogin}, + {Name: consts.OpenAPITagNameOrganization}, + {Name: consts.OpenAPITagNameDept}, + {Name: consts.OpenAPITagNamePost}, + {Name: consts.OpenAPITagNameRole}, + {Name: consts.OpenAPITagNameUser}, + {Name: consts.OpenAPITagNameMenu}, + {Name: consts.OpenAPITagNameApi}, + {Name: consts.OpenAPITagNameAuthorize}, + } +} diff --git a/internal/cmd/global.go b/internal/cmd/global.go new file mode 100644 index 0000000..fe1c37b --- /dev/null +++ b/internal/cmd/global.go @@ -0,0 +1,73 @@ +package cmd + +import ( + "context" + "github.com/gogf/gf/contrib/trace/jaeger/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gmode" + "sagooiot/internal/consts" + "sagooiot/internal/logic/analysis" + "sagooiot/pkg/cache" + "sagooiot/pkg/dcache" + "sagooiot/pkg/statistics" + "sagooiot/pkg/utility/helper" + "sagooiot/utility/validate" +) + +func AllSystemInit(ctx context.Context) { + // 设置gf运行模式 + //SetGFMode(ctx) + // 默认上海时区 + if err := gtime.SetTimeZone("Asia/Shanghai"); err != nil { + g.Log().Fatalf(ctx, "时区设置异常 err:%+v", err) + return + } + // 初始化链路追踪 + InitTrace(ctx) + // 设置缓存适配器 + cache.SetAdapter(ctx) + // 初始化统计设备数据 + statistics.InitCountDeviceData() + // 初始化系统配置参数 + err := dcache.InitSystemConfig(ctx) + if err != nil { + g.Log().Debug(ctx, "初始化系统配置参数失败") + } + + //清除设备统计缓存 + analysis.RemoveDeviceStatusCountCache(ctx) + +} + +// SetGFMode 设置gf运行模式 +func SetGFMode(ctx context.Context) { + mode := g.Cfg().MustGet(ctx, "system.mode").String() + if len(mode) == 0 { + mode = gmode.NOT_SET + } + + var modes = []string{gmode.DEVELOP, gmode.TESTING, gmode.STAGING, gmode.PRODUCT} + + // 如果是有效的运行模式,就进行设置 + if validate.InSlice(modes, mode) { + gmode.Set(mode) + } +} + +// InitTrace 初始化链路追踪 +func InitTrace(ctx context.Context) { + if !g.Cfg().MustGet(ctx, "jaeger.switch").Bool() { + return + } + + tp, err := jaeger.Init(helper.AppName(ctx), g.Cfg().MustGet(ctx, "jaeger.endpoint").String()) + if err != nil { + g.Log().Fatal(ctx, err) + } + + helper.Event().Register(consts.EventServerClose, func(ctx context.Context, args ...interface{}) { + _ = tp.Shutdown(ctx) + g.Log().Debug(ctx, "jaeger closed ..") + }) +} diff --git a/internal/cmd/initfunc.go b/internal/cmd/initfunc.go new file mode 100644 index 0000000..b9a6703 --- /dev/null +++ b/internal/cmd/initfunc.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "context" + "fmt" + "sagooiot/internal/service" + "sagooiot/network" + "sagooiot/network/core/logic/model" + + "github.com/gogf/gf/v2/frame/g" +) + +type NoDeferFunc struct { + F func(ctx context.Context) error + Desc string +} + +var InitFuncNoDeferListForIotCore = []NoDeferFunc{ + {model.InitCoreLogic, "核心处理逻辑"}, + {service.TSLTable().CreateDatabase, "时序数据库创建"}, + {service.TdLogTable().CreateStable, "时序库日志表创建"}, + {service.DevInit().InitProductForTd, "时序库产品表初始化"}, + {service.DevInit().InitDeviceForTd, "时序库设备表初始化"}, + {service.DevDevice().CacheDeviceDetailList, "缓存设备信息"}, + {service.AlarmRule().CacheAllAlarmRule, "缓存告警规则"}, + {network.ReloadNetwork, "网络服务"}, +} + +var InitFuncNoDeferListWebAdmin = []NoDeferFunc{ + {service.SysAuthorize().InitAuthorize, "系统权限"}, + {initSystemStatistics, "系统统计"}, + {service.SysInfo().ServerInfoEscalation, "集群数据"}, + {initPlugins, "插件"}, +} + +func InitSystem(ctx context.Context, noDeferFuncList []NoDeferFunc) error { + for _, funcNode := range noDeferFuncList { + g.Log().Infof(ctx, "开始初始化%s", funcNode.Desc) + if err := funcNode.F(ctx); err != nil { + return fmt.Errorf("初始化%s失败,错误原因是:%w", funcNode.Desc, err) + } else { + g.Log().Infof(ctx, "初始化%s成功", funcNode.Desc) + } + } + return nil +} + +var initFuncWithDeferList = []DeferFunc{ + {RunQueue, "消息队列"}, + {wrapperMqtt, "mqtt连接"}, +} + +func InitSystemDeferFunc(ctx context.Context) ([]func(context.Context) error, error) { + deferFuncList := make([]func(context.Context) error, len(initFuncWithDeferList)) + for index, deferFuncNode := range initFuncWithDeferList { + g.Log().Infof(ctx, "开始初始化%s", deferFuncNode.Desc) + if err, deferFunc := deferFuncNode.F(ctx); err != nil { + return nil, fmt.Errorf("初始化%s失败,错误原因是:%w", deferFuncNode.Desc, err) + } else { + deferFuncList[index] = deferFunc + g.Log().Infof(ctx, "初始化%s成功", deferFuncNode.Desc) + } + } + return deferFuncList, nil + +} diff --git a/internal/cmd/performance.go b/internal/cmd/performance.go new file mode 100644 index 0000000..3b671bf --- /dev/null +++ b/internal/cmd/performance.go @@ -0,0 +1,50 @@ +package cmd + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "os" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/internal/sse/sysenv" + "sagooiot/pkg/plugins" + "sagooiot/pkg/plugins/consts/PluginType" + "sagooiot/pkg/utility/utils" +) + +// InitSystemStatistics 初始化系统统计 +func initSystemStatistics(ctx context.Context) error { + sysenv.LocalIP, _ = utils.GetLocalIP() //获取本机IP + sysenv.PublicIP, _ = utils.GetPublicIP() //获取公网IP + SysRunDir, _ := os.Getwd() //获取当前运行目录 + sysenv.GoDiskSize = utils.DirSize(SysRunDir) //获取当前运行目录磁盘大小 + + return nil +} + +// initPlugins 初始化插件 +func initPlugins(ctx context.Context) (err error) { + var inputData = new(model.GetSysPluginsListInput) + inputData.PageSize = 1000 + inputData.Status = 1 + _, _, pluginDataList, err := service.SysPlugins().GetSysPluginsList(context.Background(), inputData) + for _, p := range pluginDataList { + //加载插件 + switch p.Types { + case PluginType.Protocol: + _, err = plugins.GetProtocolPlugin().GetProtocolByName(p.Name) + case PluginType.Notice: + _, err = plugins.GetNoticePlugin().GetNoticeByName(p.Name) + } + if err != nil { + g.Log().Debug(ctx, p.Name, "插件加载出错", err.Error()) + } + } + + //更新插件配置缓存 + err = service.SystemPluginsConfig().UpdateAllPluginsConfigCache(context.Background()) + if err != nil { + return err + } + return +} diff --git a/internal/cmd/pprof.go b/internal/cmd/pprof.go new file mode 100644 index 0000000..1e675c0 --- /dev/null +++ b/internal/cmd/pprof.go @@ -0,0 +1,36 @@ +package cmd + +import ( + "context" + "fmt" + "github.com/arl/statsviz" + "github.com/gogf/gf/v2/frame/g" + "net/http" + "os" + "runtime" + "syscall" + "time" +) + +func RunSystemAnalysis(stopSignal chan os.Signal, pprofPort string) { + // 开启性能分析 + runtime.SetMutexProfileFraction(1) // (非必需)开启对锁调用的跟踪 + runtime.SetBlockProfileRate(1) // (非必需)开启对阻塞操作的跟踪 + // 将Go程序运行时的各种内部数据进行可视化的展示,如可以展示:堆、对象、协程、GC等信息 + err := statsviz.RegisterDefault() + if err == nil { + g.Log().Infof(context.Background(), "Point your browser to http://localhost:%s/debug/statsviz/", pprofPort) + go func() { + s := &http.Server{ + Addr: ":" + pprofPort, + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 30 * time.Second, + } + if err := s.ListenAndServe(); err != nil { + fmt.Println(err) + } + stopSignal <- syscall.SIGQUIT + }() + } +} diff --git a/internal/cmd/queue.go b/internal/cmd/queue.go new file mode 100644 index 0000000..0b704ff --- /dev/null +++ b/internal/cmd/queue.go @@ -0,0 +1,32 @@ +package cmd + +import ( + "context" + "sagooiot/internal/mqtt" + "sagooiot/internal/queues" + _ "sagooiot/internal/queues" + "sagooiot/module" + "sagooiot/pkg/worker" +) + +func RunQueue(ctx context.Context) (error, func(context.Context) error) { + worker.TasksInstance() //启用系统的任务队列 + queues.Run() //启用系统的消息队列 + module.WorkerRun() //启用模块应用的任务 + return nil, nil +} + +func wrapperMqtt(ctx context.Context) (error, func(context.Context) error) { + if err := mqtt.InitSystemMqtt(); err != nil { + return err, nil + } + return nil, func(ctx context.Context) error { + mqtt.Close() + return nil + } +} + +type DeferFunc struct { + F func(ctx context.Context) (error, func(context.Context) error) + Desc string +} diff --git a/internal/cmd/router/analysis.go b/internal/cmd/router/analysis.go new file mode 100644 index 0000000..78ac637 --- /dev/null +++ b/internal/cmd/router/analysis.go @@ -0,0 +1,22 @@ +package router + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + analysisController "sagooiot/internal/controller/analysis" + "sagooiot/internal/service" +) + +// Analysis 分析统计相关的接口 +func Analysis(ctx context.Context, group *ghttp.RouterGroup) { + group.Group("/analysis", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + analysisController.Device, // 设备相关统计 + analysisController.Alarm, // 设备相关相关统计 + analysisController.Product, // 产品相关统计 + analysisController.DeviceData, // 设备数据相关统计 + + ) + }) +} diff --git a/internal/cmd/router/business.go b/internal/cmd/router/business.go deleted file mode 100644 index 7ad781a..0000000 --- a/internal/cmd/router/business.go +++ /dev/null @@ -1,20 +0,0 @@ -package router - -import ( - "context" - envirotronicsController "github.com/sagoo-cloud/sagooiot/internal/controller/envirotronics" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/net/ghttp" -) - -// Business 业务相关功能的路由 -func Business(ctx context.Context, group *ghttp.RouterGroup) { - //环测相关路由 - group.Group("/envirotronics", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - envirotronicsController.Weather, // 天气监测 - ) - }) -} diff --git a/internal/cmd/router/iot.go b/internal/cmd/router/iot.go new file mode 100644 index 0000000..14c6821 --- /dev/null +++ b/internal/cmd/router/iot.go @@ -0,0 +1,77 @@ +package router + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + alarmController "sagooiot/internal/controller/alarm" + networkController "sagooiot/internal/controller/network" + noticeController "sagooiot/internal/controller/notice" + productController "sagooiot/internal/controller/product" + tdengineController "sagooiot/internal/controller/tdengine" + + "sagooiot/internal/service" +) + +// Iot iot功能的路由 +func Iot(ctx context.Context, group *ghttp.RouterGroup) { + + // 产品设备相关路由 + group.Group("/product", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + productController.Category, // 产品分类 + productController.Product, // 产品 + productController.Device, // 设备 + productController.DeviceTag, // 设备标签 + productController.DeviceLog, // 设备日志 + productController.DeviceFunction, // 设备功能执行 + productController.DeviceProperty, // 设备属性设置 + productController.TSLDataType, // 物模型:数据类型 + productController.TSLProperty, // 物模型:属性 + productController.TSLFunction, // 物模型:功能 + productController.TSLEvent, // 物模型:事件 + productController.TSLTag, // 物模型:标签 + productController.DeviceTree, // 设备树 + productController.TSLImport, // 物模型:导入/导出 + ) + }) + + // 告警相关路由 + group.Group("/alarm", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + alarmController.AlarmLevel, // 告警级别 + alarmController.AlarmRule, // 告警规则 + alarmController.AlarmLog, // 告警日志 + ) + }) + + // 网络通道相关路由 + group.Group("/network", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + networkController.Tunnel, // 通讯通道管理 + networkController.Server, // 通讯服务管理 + + ) + }) + + //时序数据库相关路由 + group.Group("/tdengine", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + tdengineController.TdEngine, //websocket + ) + }) + + //通知服务相关路由 + group.Group("/notice", func(group *ghttp.RouterGroup) { + group.Middleware(service.Middleware().Auth) + group.Bind( + noticeController.NoticeInfo, + noticeController.NoticeConfig, + noticeController.NoticeTemplate, + noticeController.NoticeLog, + ) + }) +} diff --git a/internal/cmd/router/system.go b/internal/cmd/router/system.go index a91d7ce..1c80e26 100644 --- a/internal/cmd/router/system.go +++ b/internal/cmd/router/system.go @@ -2,19 +2,10 @@ package router import ( "context" - alarmController "github.com/sagoo-cloud/sagooiot/internal/controller/alarm" - commonController "github.com/sagoo-cloud/sagooiot/internal/controller/common" - networkController "github.com/sagoo-cloud/sagooiot/internal/controller/network" - noticeController "github.com/sagoo-cloud/sagooiot/internal/controller/notice" - productController "github.com/sagoo-cloud/sagooiot/internal/controller/product" - sourceController "github.com/sagoo-cloud/sagooiot/internal/controller/source" - systemController "github.com/sagoo-cloud/sagooiot/internal/controller/system" - tdengineController "github.com/sagoo-cloud/sagooiot/internal/controller/tdengine" - thingController "github.com/sagoo-cloud/sagooiot/internal/controller/thing" - - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/gogf/gf/v2/net/ghttp" + commonController "sagooiot/internal/controller/common" + systemController "sagooiot/internal/controller/system" + "sagooiot/internal/service" ) // System 系统默认功能的路由,不含业务属性的 @@ -22,10 +13,10 @@ func System(ctx context.Context, group *ghttp.RouterGroup) { //系统登录路由 group.Group("/", func(group *ghttp.RouterGroup) { group.Bind( - systemController.Login, // 登录 - systemController.Captcha, // 验证码 - commonController.SysInfo, //系统信息 - + systemController.Login, // 登录 + systemController.Captcha, // 验证码 + commonController.SysInfo, //系统信息 + commonController.CheckAuth, //权限验证 ) }) @@ -37,64 +28,6 @@ func System(ctx context.Context, group *ghttp.RouterGroup) { commonController.ConfigData, commonController.DictType, commonController.DictData, - commonController.BaseDbLink, //数据源管理 - commonController.CityData, //城市管理 - ) - }) - - // 物联概览相关路由 - group.Group("/thing", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - thingController.DataOverview, // 物联概览 - ) - }) - - // 产品设备相关路由 - group.Group("/product", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - productController.Category, // 产品分类 - productController.Product, // 产品 - productController.Device, // 设备 - productController.DeviceTag, // 设备标签 - productController.DeviceLog, // 设备日志 - productController.TSLDataType, // 物模型:数据类型 - productController.TSLProperty, // 物模型:属性 - productController.TSLFunction, // 物模型:功能 - productController.TSLEvent, // 物模型:事件 - productController.TSLTag, // 物模型:标签 - ) - }) - - // 告警相关路由 - group.Group("/alarm", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - alarmController.AlarmLevel, // 告警级别 - alarmController.AlarmRule, // 告警规则 - alarmController.AlarmLog, // 告警日志 - ) - }) - - // 数据源相关路由 - group.Group("/source", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - sourceController.DataSource, // 数据源 - sourceController.DataNode, // 数据节点 - sourceController.DataTemplate, // 数据模型 - sourceController.DataTemplateNode, // 数据模型节点 - ) - }) - - // 网络通道相关路由 - group.Group("/network", func(group *ghttp.RouterGroup) { - //group.Middleware(service.Middleware().Auth) - group.Bind( - networkController.Tunnel, // 通讯通道管理 - networkController.Server, // 通讯服务管理 - ) }) @@ -107,6 +40,9 @@ func System(ctx context.Context, group *ghttp.RouterGroup) { systemController.SysPost, // 岗位 systemController.SysUser, // 用户 systemController.SysMenu, // 菜单 + systemController.SysMenuButton, // 菜单按钮 + systemController.SysMenuColumn, // 菜单列表 + systemController.SysMenuApi, // 菜单API systemController.SysApi, // 接口 systemController.SysAuthorize, //权限管理 systemController.SysOrganization, //组织管理 @@ -115,32 +51,15 @@ func System(ctx context.Context, group *ghttp.RouterGroup) { systemController.SysJob, //定时任务管理 - systemController.SysMonitor, //服务监控 systemController.SysUserOnline, //在线用户 systemController.SysNotifications, //消息中心 systemController.SysPlugins, //插件管理 systemController.SysPluginsConfig, //插件配置管理 - ) - }) + systemController.SysMessage, // 通知中心 + systemController.SysCertificate, // 证书管理 - //时序数据库相关路由 - group.Group("/tdengine", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - tdengineController.TdEngine, //websocket - ) - }) - - //通知服务相关路由 - group.Group("/notice", func(group *ghttp.RouterGroup) { - group.Middleware(service.Middleware().Auth) - group.Bind( - noticeController.NoticeInfo, - noticeController.NoticeConfig, - noticeController.NoticeTemplate, - noticeController.NoticeLog, ) }) diff --git a/internal/cmd/server.go b/internal/cmd/server.go new file mode 100644 index 0000000..99df947 --- /dev/null +++ b/internal/cmd/server.go @@ -0,0 +1,105 @@ +package cmd + +import ( + "context" + "expvar" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gmode" + "os" + "sagooiot/internal/cmd/router" + "sagooiot/internal/service" + "sagooiot/internal/sse" + "sagooiot/module" + "syscall" +) + +func RunServer(ctx context.Context, stopSignal chan os.Signal) { + var s = g.Server() + // 自定义丰富文档 + enhanceOpenAPIDoc(s) + // 错误状态码接管 + s.BindStatusHandler(404, func(r *ghttp.Request) { + r.Response.Writeln("404 - 没有找到…") + }) + s.BindStatusHandler(403, func(r *ghttp.Request) { + r.Response.Writeln("403 - 拒绝显示") + }) + + // exp var 监控 + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/debug/vars", ghttp.WrapH(expvar.Handler())) + }) + + // 静态目录设置 + uploadPath := g.Cfg().MustGet(ctx, "system.upload.path").String() + if uploadPath == "" { + g.Log().Fatal(ctx, "文件上传配置路径不能为空") + } + + // HOOK, 开发阶段禁止浏览器缓存,方便调试 + if gmode.IsDevelop() { + s.BindHookHandler("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) { + r.Response.Header().Set("Cache-Control", "no-store") + }) + } + + //操作日志 + s.BindHookHandler("/*", ghttp.HookAfterOutput, func(r *ghttp.Request) { + service.Middleware().OperationLog(r) + }) + + //sse 实时数据推送 + s.Group("/subscribe", func(group *ghttp.RouterGroup) { + group.GET("/sysenv", sse.SysenvMessageEvent) + group.GET("/redisinfo", sse.RedisInfoMessageEvent) + group.GET("/mysqlinfo", sse.MysqlInfoMessageEvent) + group.GET("/sysMessage", sse.SysMessageEntvt) + group.GET("/logInfo", sse.LogInfoEvent) + }) + + s.Group("/api/v1", func(group *ghttp.RouterGroup) { + group.Middleware( + service.Middleware().Ctx, + service.Middleware().ResponseHandler, + service.Middleware().MiddlewareCORS, + service.Middleware().I18n, + ) + + router.System(ctx, group) //系统默认功能的路由 + router.Iot(ctx, group) //Iot功能的路由 + router.Analysis(ctx, group) //分析统计功能的路由 + module.Router(ctx, group) //加载模块的路由 + + }) + + // pprof性能分析 + enablePProf := g.Cfg().MustGet(context.Background(), "system.enablePProf").Bool() + if enablePProf { + // exp var 监控 + s.Group("/", func(group *ghttp.RouterGroup) { + group.GET("/debug/vars", ghttp.WrapH(expvar.Handler())) + }) + s.EnablePProf() //打开pprof性能分析工具,不需要的时候可以注掉 + } + + go func() { + defer func() { + if err := recover(); err != nil { + fmt.Println("panic 产生,错误:", err) + } + }() + // https + https := g.Cfg().MustGet(ctx, "server.https").Bool() + if https { + certFile := g.Cfg().MustGet(ctx, "server.httpsCertFile").String() + keyFile := g.Cfg().MustGet(ctx, "server.httpsKeyFile").String() + s.EnableHTTPS(certFile, keyFile) + } + + s.Run() + stopSignal <- syscall.SIGQUIT + }() + return +} diff --git a/internal/consts/alarm.go b/internal/consts/alarm.go new file mode 100644 index 0000000..f52736f --- /dev/null +++ b/internal/consts/alarm.go @@ -0,0 +1,29 @@ +package consts + +const ( + AlarmTriggerModeDevice = iota + 1 // 触发方式:设备触发 + AlarmTriggerModeCron // 触发方式:定时触发 +) + +const ( + AlarmTriggerTypeOnline = iota + 1 // 触发类型:设备上线 + AlarmTriggerTypeOffline // 触发类型:设备离线 + AlarmTriggerTypeProperty // 触发类型:属性上报 + AlarmTriggerTypeEvent // 触发类型:事件上报 +) + +const ( + OperatorEq = "eq" // 操作符:等于 + OperatorNe = "ne" // 操作符:不等于 + OperatorGt = "gt" // 操作符:大于 + OperatorGte = "gte" // 操作符:大于等于 + OperatorLt = "lt" // 操作符:小于 + OperatorLte = "lte" // 操作符:小于等于 + OperatorBet = "bet" // 操作符:在...之间 + OperatorNbet = "nbet" // 操作符:不在...之间 +) + +const ( + AlarmRuleStatusOff int = iota // 告警规则状态:未启用 + AlarmRuleStatusOn // 告警规则状态:已启用 +) diff --git a/internal/consts/analysis.go b/internal/consts/analysis.go new file mode 100644 index 0000000..0db98d4 --- /dev/null +++ b/internal/consts/analysis.go @@ -0,0 +1,22 @@ +package consts + +const ( + // AnalysisDeviceCountPrefix 设备统计前缀 + AnalysisDeviceCountPrefix = "analysis:device:count:" + AnalysisProductCountPrefix = "analysis:product:count:" + ProductDeviceCount = "ProductDeviceCount:" + + // 设备统计数据项 + + TodayMessageVolume = "TodayMessageVolume:" // 今日消息量 + DeviceTotal = "DeviceTotal" // 设备总数 + DeviceDisable = "DeviceDisable" // 在线设备数 + + AnalysisAlarmCountPrefix = "analysis:alarm:count:" + + AlarmTotal = "AlarmTotal:" // 设备总数 + AlarmMonthsMessageVolume = "MonthsMessageVolume:" // 月度告警 + AlarmTodayMessageVolume = "TodayMessageVolume" // 今日告警 + AlarmLevelMessageVolume = "AlarmLevelMessageVolume:" // 今日告警 + +) diff --git a/internal/consts/cache.go b/internal/consts/cache.go index 492d574..161be4d 100644 --- a/internal/consts/cache.go +++ b/internal/consts/cache.go @@ -9,35 +9,52 @@ const ( CacheModelRedis = "redis" // CacheSysDict 字典缓存菜单KEY - CacheSysDict = "sysDict" + CacheSysDict = "SystemCache:sysDict" // CacheSysRole 角色缓存key - CacheSysRole = "sysRole" + CacheSysRole = "SystemCache:sysRole" // CacheSysDept 部门缓存key - CacheSysDept = "sysDept" + CacheSysDept = "SystemCache:sysDept" // CacheSysAuthTag 权限缓存TAG标签 - CacheSysAuthTag = "sysAuthTag" + CacheSysAuthTag = "SystemCache:sysAuthTag" // CacheSysDictTag 字典缓存标签 - CacheSysDictTag = "sysDictTag" - // CacheSysConfigTag 系统参数配置 - CacheSysConfigTag = "sysConfigTag" + CacheSysDictTag = "SystemCache:sysDictTag" //CacheSysMenu 系统菜单 - CacheSysMenu = "sysMenu" + CacheSysMenu = "SystemCache:sysMenu" //CacheSysMenuButton 系统菜单按钮 - CacheSysMenuButton = "sysMenuButton" + CacheSysMenuButton = "SystemCache:sysMenuButton:" //CacheSysMenuColumn 系统菜单按钮 - CacheSysMenuColumn = "sysMenuColumn" + CacheSysMenuColumn = "SystemCache:sysMenuColumn:" //CacheSysAuthorize 系统权限 - CacheSysAuthorize = "sysAuthorize" + CacheSysAuthorize = "SystemCache:sysAuthorize:" //CacheSysMenuApi 系统API与菜单绑定关系表 - CacheSysMenuApi = "sysMenuApi" + CacheSysMenuApi = "SystemCache:sysMenuApi:" //CacheSysApi 系统API - CacheSysApi = "sysApi" + CacheSysApi = "SystemCache:sysApi" //CacheUserAuthorize 用户权限 - CacheUserAuthorize = "userAuthorize" + CacheUserAuthorize = "SystemCache:userAuthorize" //CacheUserInfo 用户信息 - CacheUserInfo = "userInfo" + CacheUserInfo = "SystemCache:userInfo" + + //CacheIpBlackList IP访问黑名单 + CacheIpBlackList = "SystemCache:sysIpBlackList" + + //CacheDeviceOnline 下面的是网络部分用到的 + CacheDeviceOnline = "networkDeviceOnline" + + // 告警规则 + CacheAlarmRule = "AlarmRule:rule" + // 服务器信息 + CacheServerInfo = "SystemCache:server_info" + + CacheSysErrorPrefix = "SysErrorPwdNum:" + + // 插件配置缓存 + PluginsTypeName = "plugins:%s:%s" + + // GoFrame ORM缓存前缀 + CacheGfOrmPrefix = "SelectCache:" ) diff --git a/internal/consts/config.go b/internal/consts/config.go index c0c0da5..490831a 100644 --- a/internal/consts/config.go +++ b/internal/consts/config.go @@ -1,5 +1,51 @@ package consts +// 系统参数KEY常量 const ( - PageSize = 10 //分页长度 + SysUploadFileDomain = "sys.uploadFile.domain" + IsAutoRunJob = "sys.auto.run.job" + SysOpenapiSecretkey = "sys.openapi.secretkey" + SysMapLngAndLat = "sys.map.lngAndLat" //地图中心点经纬度 + SysMapAccessKey = "sys.map.access.key" //百度地图访问密钥 + SysSystemName = "sys.system.name" + HomePageRoute = "homePageRoute" + SysColumnSwitch = "sys.column.switch" //列表开关 + SysButtonSwitch = "sys.button.switch" //按钮开关 + SysApiSwitch = "sys.api.switch" //api开关 + SysSystemCopyright = "sys.system.copyright" + SysSystemLogo = "sys.system.logo" + SysSystemLoginPic = "sys.system.login.pic" + SysSystemLogoMini = "sys.system.logo.mini" + SysIsSingleLogin = "sys.is.single.login" //是否单一登录 + SysTokenExpiryDate = "sys.token.expiry.date" //TOKEN过期时间 + SysPasswordChangePeriod = "sys.password.change.period" //密码更换周期 + SysPasswordChangePeriodSwitch = "sys.password.change.period.switch" //密码更换周期开关 + SysPasswordErrorNum = "sys.password.error.num" //密码输入错误次数 + SysAgainLoginDate = "sys.again.login.date" //允许再次登录时间 + SysPasswordMinimumLength = "sys.password.minimum.length" //密码长度 + SysRequireComplexity = "sys.require.complexity" //是否包含复杂字符 + SysRequireDigit = "sys.require.digit" //是否包含数字 + SysRequireLowercaseLetter = "sys.require.lowercase.letter" //是否包含小写字母 + SysRequireUppercaseLetter = "sys.require.uppercase.letter" //是否包含大写字母 + SysChangePasswordForFirstLogin = "sys.change.password.for.first.login" //首次登录是否更改密码开关 + SysIsSecurityControlEnabled = "sys.is.security.control.enabled" //是否启用安全控制 + SysIsRsaEnabled = "sys.is.rsa.enabled" //是否启用RSA + SYSUPLOADFILEWAY = "sys.uploadFile.way" //文件上传方式 +) + +// MINIO +const ( + MinioDomain = "minio.domain" + MinioApiDomain = "minio.api.domain" + MinioAccessKeyId = "minio.accessKeyId" + MinioSecretAccessKey = "minio.secretAccessKey" + MinioUseSsl = "minio.useSSL" + MinioBucketName = "minio.bucketName" + MinioLocation = "minio.location" +) + +// 设备相关配置的参数 +const ( + DeviceDataDelayedStorageTime = "device.data.delayed.storage.time" //延迟落库时间 + DeviceDefaultTimeoutTime = "device.default.timeout.time" //设备默认超时时间 ) diff --git a/internal/consts/consts.go b/internal/consts/consts.go index ad3fdfd..d6bb0ba 100644 --- a/internal/consts/consts.go +++ b/internal/consts/consts.go @@ -1,17 +1,8 @@ package consts const ( - Version = "v0.2.0" // 当前服务版本(用于模板展示) - CaptchaDefaultName = "CaptchaDefaultName" // 验证码默认存储空间名称 - ContextKey = "ContextKey" // 上下文变量存储键名,前后端系统共享 - FileMaxUploadCountMinute = 10 // 同一用户1分钟之内最大上传数量 - DefaultPageSize = 10 //默认分页条数 -) - -// 系统启动OR禁用常量 -const ( - Start = 1 //启动 - Disabled = 0 //禁用 + ContextKey = "ContextKey" // 上下文变量存储键名,前后端系统共享 + PageSize = 10 //默认分页条数 ) // 权限类型常量 @@ -45,16 +36,13 @@ const ( ) const ( - Weather = 1 //天气 - LoopRegulation = 2 //环路监管 - LoopMap = 3 //分布图 - Energy = 4 //能耗分析 + Weather = 1 //天气 + ) // 服务状态 const ( - ServerStatusOffline = 0 - ServerStatusOnline = 1 + ServerStatusOnline = 1 ) // ServerListLimit 服务限制 @@ -62,25 +50,29 @@ const ( ServerListLimit = 10000 ) -// 业务单元 +const ApiTypes = "api_types" + +// 默认的插件协议 const ( - PLOT = "plot" - Floor = "floor" - Unit = "unit" + DefaultProtocol = "SagooMqtt" ) -// 系统参数KEY常量 const ( - IsAutoRunJob = "sys.auto.run.job" - IsOpenAccessControl = "sys.access.control" + TokenAuth = "token_auth" + AKSK = "aksk" ) -// 默认的插件协议 +// 文件路径 const ( - DefaultProtocol = "SagooMqtt" + LogPath = "./resource/log/run/" + RunLogPath = "./var/" + MysqlLogPath = "./resource/log/sql/" ) +// RSA 公私钥文件路径 const ( - TokenAuth = "token_auth" - AKSK = "aksk" + RsaPublicKeyFile = "resource/rsa/public.pem" + RsaPrivateKeyFile = "resource/rsa/private.pem" + RsaOAEP = "OAEP" + RsaPKCS1v15 = "PKCS1v15" ) diff --git a/internal/consts/content.go b/internal/consts/content.go deleted file mode 100644 index df2ecce..0000000 --- a/internal/consts/content.go +++ /dev/null @@ -1,13 +0,0 @@ -package consts - -const ( - ContentListDefaultSize = 10 - ContentListMaxSize = 50 - ContentSortDefault = 0 // 排序:按照创建时间 - ContentSortActive = 1 // 排序:按照更新时间 - ContentSortHot = 2 // 排序:按照浏览量 - ContentSortScore = 3 // 排序:按照搜索结果关联性 - ContentTypeArticle = "article" - ContentTypeAsk = "ask" - ContentTypeTopic = "topic" -) diff --git a/internal/consts/database.go b/internal/consts/database.go new file mode 100644 index 0000000..2852ccd --- /dev/null +++ b/internal/consts/database.go @@ -0,0 +1,7 @@ +package consts + +const ( + DataBaseGroup = "default" //数据源默认分组 + DatabaseTypeMysql = "mysql" // mysql + DatabaseTypePostgresql = "pgsql" // postgresql +) diff --git a/internal/consts/dataview.go b/internal/consts/dataview.go deleted file mode 100644 index ae815d8..0000000 --- a/internal/consts/dataview.go +++ /dev/null @@ -1,6 +0,0 @@ -package consts - -const ( - // IotOverviewIndex iot概览指标 - IotOverviewIndex = "system.iot.overview.index" -) diff --git a/internal/consts/date.go b/internal/consts/date.go new file mode 100644 index 0000000..b18a0d4 --- /dev/null +++ b/internal/consts/date.go @@ -0,0 +1,6 @@ +package consts + +const ( + TimeFormatCompact = "20060102150405" + TimeFormatStandard = "2006-01-02 15:04:05" +) diff --git a/internal/consts/dcache.go b/internal/consts/dcache.go new file mode 100644 index 0000000..5af671d --- /dev/null +++ b/internal/consts/dcache.go @@ -0,0 +1,19 @@ +package consts + +const ( + ProductDetailInfoPrefix = "ProductDetailInfo:" //产品详细信息缓存KEY前缀 + DeviceDetailInfoPrefix = "DeviceDetailInfo:" //设备详细信息缓存KEY前缀 + + // 设备在线状态超时,120秒 + DeviceOnlineTimeOut = 120 + // 设备状态缓存KEY前缀 + DeviceStatusPrefix = "deviceStatus:" + + //设备告警规则缓存KEY前缀 + DeviceAlarmRulePrefix = "deviceAlarmRule:" + // 设备告警日志缓存KEY前缀 + DeviceAlarmLogPrefix = "deviceAlarmLog:" + + // 系统配置参数缓存KEY前缀 + SystemConfigPrefix = "systemConfig:" +) diff --git a/internal/consts/energy.go b/internal/consts/energy.go deleted file mode 100644 index 537781a..0000000 --- a/internal/consts/energy.go +++ /dev/null @@ -1,7 +0,0 @@ -package consts - -const ( - LossWaterId = "energy.loss.water" - StationInfos = "energy.station.infos" - StationLists = "energy.station.lists" -) diff --git a/internal/consts/event.go b/internal/consts/event.go new file mode 100644 index 0000000..1386a00 --- /dev/null +++ b/internal/consts/event.go @@ -0,0 +1,5 @@ +package consts + +const ( + EventServerClose = "server.close" // 服务关闭事件 +) diff --git a/internal/consts/interact.go b/internal/consts/interact.go deleted file mode 100644 index 0b66872..0000000 --- a/internal/consts/interact.go +++ /dev/null @@ -1,8 +0,0 @@ -package consts - -const ( - InteractTypeZan = 0 - InteractTypeCai = 1 - InteractTargetTypeContent = "content" - InteractTargetTypeReply = "reply" -) diff --git a/internal/consts/product.go b/internal/consts/product.go new file mode 100644 index 0000000..4646c77 --- /dev/null +++ b/internal/consts/product.go @@ -0,0 +1,6 @@ +package consts + +const ( + GetDetailProductOutput = "GetDetailProductOutput:" + GetDetailDeviceOutput = "GetDetailDeviceOutput:" +) diff --git a/internal/consts/queue.go b/internal/consts/queue.go new file mode 100644 index 0000000..5a00258 --- /dev/null +++ b/internal/consts/queue.go @@ -0,0 +1,9 @@ +package consts + +// 消息队列 +const ( + QueueRequestLogTopic = "sagooiot_request_log" // 访问日志 + QueueDeviceAlarmLogTopic = "device_alarm_log" // 设备日志 + QueueDeviceDataSaveTopic = "task.device.data.save" // 设备数据保存 + QueueDeviceStatusInfoUpdate = "task.device.status.info.update" // 设备信息更新 +) diff --git a/internal/consts/scene.go b/internal/consts/scene.go new file mode 100644 index 0000000..6452709 --- /dev/null +++ b/internal/consts/scene.go @@ -0,0 +1,24 @@ +package consts + +const ( + SceneTypeManual = "manual" + SceneTypeTimer = "timer" + SceneTypeDevice = "device" + + SceneDefinition = "definition" + SceneActionConf = "action" + + // 场景触发类型 + SceneTriggerOnLine = "onLine" + SceneTriggerOffLine = "offLine" + SceneTriggerReportAttribute = "reportAttribute" + SceneTriggerReportEvent = "reportEvent" + + // 场景动作类型 + SceneActionTypeDeviceOutput = "deviceOutput" + SceneActionTypeSendNotice = "sendNotice" + SceneActionTypeCallWebService = "callWebService" + SceneActionTypeTriggerAlarm = "triggerAlarm" + SceneActionTypeDelayExecution = "delayExecution" + SceneActionTypeTriggerCustomEvent = "triggerCustomEvent" +) diff --git a/internal/consts/server_state.go b/internal/consts/server_state.go new file mode 100644 index 0000000..37eba30 --- /dev/null +++ b/internal/consts/server_state.go @@ -0,0 +1,6 @@ +package consts + +const ( + TunnelIsOffline = iota + TunnelIsOnLine +) diff --git a/internal/consts/td_engine.go b/internal/consts/td_engine.go new file mode 100644 index 0000000..97ae0bc --- /dev/null +++ b/internal/consts/td_engine.go @@ -0,0 +1,16 @@ +package consts + +// td 产品表前缀 +const TdProductPrefix = "product_" + +// td 设备表前缀 +const TdDevicePrefix = "device_" + +// td 日志表前缀 +const TdLogPrefix = "log_" + +// td 属性前缀 +const TdPropertyPrefix = "p_" + +// td tag前缀 +const TdTagPrefix = "t_" diff --git a/internal/consts/topic.go b/internal/consts/topic.go index a953791..1ff5156 100644 --- a/internal/consts/topic.go +++ b/internal/consts/topic.go @@ -2,15 +2,39 @@ package consts import "fmt" +const ( + MsgTypeOnline = "设备上线" + MsgTypeOffline = "设备下线" + MsgTypeEvent = "事件上报" + MsgTypePropertyRead = "读取属性" + MsgTypePropertyReadReply = "读取属性回复" + MsgTypePropertyWrite = "修改属性" + MsgTypePropertyWriteReply = "修改属性回复" + MsgTypeFunctionSend = "方法调用" + MsgTypeFunctionReply = "方法调用回复" + MsgTypePropertyReport = "属性上报" + MsgTypePropertySet = "属性设置" + MsgTypePropertySetReply = "属性设置回复" + MsgTypeGatewayBatch = "网关批量上报" + MsgTypeGatewayBatchReply = "网关批量上报回复" + MsgTypeRegister = "设备注册" + MsgTypeUnRegister = "设备解除注册" + + MsgTypeDeviceInForm = "设备上报版本信息" + MsgTypeDeviceUpgradeProcess = "设备更新进度" + + MsgTypeConfigPush = "设置远程配置下发" + MsgTypeConfigPushReply = "设置远程配置下发回复" + + MsgTypeConfigGet = "设置远程请求" +) + +// todo delete code type ( Topic string Action string ) -const ( - TopicDeviceData Topic = "device/+/#" -) - const ( ActionError Action = "error" ActionOnline Action = "online" @@ -20,104 +44,42 @@ const ( ActionTunnel Action = "tunnel" ) -func GetWrapperTopic(topic Topic, action Action, id int) string { +func GetWrapperTopic(topic Topic, action Action, id string) string { return fmt.Sprintf(string(topic), id, action) } -func GetDataBusWrapperTopic(productKey, deviceKey string, topic Topic) string { - return fmt.Sprintf("/device/%s/%s%s", productKey, deviceKey, topic) -} - -const CommonDataBusPrefix = "/device/+/+" - const ( - DataBusOnline Topic = "/online" - DataBusOffline Topic = "/offline" - - DataBusEvent Topic = "/message/event/{eventId}" - - DataBusPropertyReport Topic = "/message/property/report" - - DataBusPropertyRead Topic = "/message/send/property/read" - DataBusPropertyReadReply Topic = "/message/property/read/reply" - - DataBusPropertyWrite Topic = "/message/send/property/write" - DataBusPropertyWriteReply Topic = "/message/property/write/reply" - - DataBusFunctionSend Topic = "/message/send/function" - DataBusFunctionReply Topic = "/message/function/reply" - - DataBusRegister Topic = "/register" - DataBusUnRegister Topic = "/unregister" - - DataBusChildDeviceMessage Topic = "/message/children/{childrenDeviceId}/{topic}" - DataBusChildDeviceMessageReply Topic = "/message/children/reply/{childrenDeviceId}/{topic}" - - DataBusDirect Topic = "/message/direct" - DataBusUpdate Topic = "/message/tags/update" - - DataBusFirmwarePull Topic = "/firmware/pull" - DataBusFirmwarePullReply Topic = "/firmware/pull/reply" - - DataBusFirmwarePush Topic = "/firmware/push" - DataBusFirmwarePushReply Topic = "/firmware/push/reply" - - DataBusFirmwareReport Topic = "/firmware/report" - - DataBusFirmwareProgress Topic = "/firmware/progress" - - DataBusLog Topic = "/message/log" - - DataBusMetadataDerived Topic = "/metadata/derived" -) - -const ( - DataBusServer Topic = "/system/server/%d/%s" - DataBusTunnel Topic = "/system/tunnel/%d/%s" -) - -var topicToDescMap = map[Topic]string{ - DataBusOnline: "设备上线", - DataBusOffline: "设备下线", - DataBusEvent: "事件上报", - DataBusPropertyRead: "读取属性", - DataBusPropertyReadReply: "读取属性回复", - DataBusPropertyWrite: "修改属性", - DataBusPropertyWriteReply: "修改属性回复", - DataBusFunctionSend: "方法调用", - DataBusFunctionReply: "方法调用回复", - DataBusPropertyReport: "属性上报", - DataBusChildDeviceMessage: "子设备消息", - DataBusChildDeviceMessageReply: "子设备消息回复", - DataBusRegister: "设备注册", - DataBusUnRegister: "设备解除注册", -} - -const ( - Online = "online" - Offline = "offline" - Event = "event" - PropertyRead = "property_read" - PropertyReadReply = "property_read_reply" - PropertyWrite = "property_write" - PropertyWriteReply = "property_write_reply" - FunctionSend = "function_send" - FunctionReply = "function_reply" - PropertyReport = "property_report" - ChildDeviceMessage = "child_device_message" - ChildDeviceMessageReply = "child_device_message_reply" - Register = "register" - UnRegister = "un_register" + DataBusServer Topic = "/system/server/%d/%s" + DataBusTunnel Topic = "/system/tunnel/%d/%s" + DataBusServerTunnel Topic = "/system/server/tunnel/%s/%s" + + DataBusUpgradeInfo Topic = "/upgrade/get" + DataBusUpgradeInfoReply Topic = "/upgrade/get/reply" + IssueUpgradeCmd Topic = "/system/server/upgrade/%s/%s" // 下发ota升级信息 + PostUpgradeResult Topic = "ota/device/upgrade/+/+" // 升级结果上报 {version:"1.0.1"} ) -func GetTopicType(topic Topic) string { - return topicToDescMap[topic] -} - func GetTopicTypes() []string { - var topicTypes = make([]string, 0) - for _, t := range topicToDescMap { - topicTypes = append(topicTypes, t) + return []string{ + MsgTypeOnline, + MsgTypeOffline, + MsgTypeEvent, + MsgTypePropertyRead, + MsgTypePropertyReadReply, + MsgTypePropertyWrite, + MsgTypePropertyWriteReply, + MsgTypeFunctionSend, + MsgTypeFunctionReply, + MsgTypePropertyReport, + MsgTypeGatewayBatch, + MsgTypeGatewayBatchReply, + MsgTypeRegister, + MsgTypeUnRegister, + MsgTypeDeviceInForm, + MsgTypeDeviceUpgradeProcess, + + MsgTypeConfigPush, + MsgTypeConfigPushReply, + MsgTypeConfigGet, } - return topicTypes } diff --git a/internal/consts/tsl_type.go b/internal/consts/tsl_type.go index 49ab6e7..1a62c9c 100644 --- a/internal/consts/tsl_type.go +++ b/internal/consts/tsl_type.go @@ -1,14 +1,16 @@ package consts const ( - TypeInt = "int" - TypeLong = "long" - TypeFloat = "float" - TypeDouble = "double" - TypeText = "text" - TypeBool = "boolean" - TypeDate = "date" - TypeEnum = "enum" - TypeArray = "array" - TypeObject = "object" + TypeInt = "int" + TypeLong = "long" + TypeFloat = "float" + TypeDouble = "double" + TypeText = "text" + TypeString = "string" + TypeBool = "boolean" + TypeDate = "date" + TypeTimestamp = "timestamp" + TypeEnum = "enum" + TypeArray = "array" + TypeObject = "object" ) diff --git a/internal/consts/upload.go b/internal/consts/upload.go index ddfbae2..2c14e0a 100644 --- a/internal/consts/upload.go +++ b/internal/consts/upload.go @@ -15,4 +15,5 @@ const ( SourceTencent // 上传至腾讯云 SourceAli // 上传到阿里云 SourceQiniu // 上传到七牛云 + SourceMinio // 上传至MinIO ) diff --git a/internal/consts/user.go b/internal/consts/user.go deleted file mode 100644 index c1564d4..0000000 --- a/internal/consts/user.go +++ /dev/null @@ -1,10 +0,0 @@ -package consts - -const ( - UserStatusOk = 0 // 用户状态正常 - UserStatusDisabled = 1 // 用户状态禁用 - UserGenderUnknown = 0 // 性别: 未知 - UserGenderMale = 1 // 性别: 男 - UserGenderFemale = 2 // 性别: 女 - UserLoginUrl = "/login" -) diff --git a/internal/controller/alarm/alarm_level.go b/internal/controller/alarm/alarm_level.go index bd92c05..7b8f05d 100644 --- a/internal/controller/alarm/alarm_level.go +++ b/internal/controller/alarm/alarm_level.go @@ -2,8 +2,8 @@ package alarm import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/alarm" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/alarm" + "sagooiot/internal/service" ) var AlarmLevel = cAlarmLevel{} diff --git a/internal/controller/alarm/alarm_log.go b/internal/controller/alarm/alarm_log.go index de443ba..e8dfaf2 100644 --- a/internal/controller/alarm/alarm_log.go +++ b/internal/controller/alarm/alarm_log.go @@ -2,8 +2,10 @@ package alarm import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/alarm" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/alarm" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var AlarmLog = cAlarmLog{} @@ -21,7 +23,9 @@ func (c *cAlarmLog) Detail(ctx context.Context, req *alarm.AlarmLogDetailReq) (r } func (c *cAlarmLog) List(ctx context.Context, req *alarm.AlarmLogListReq) (res *alarm.AlarmLogListRes, err error) { - out, err := service.AlarmLog().List(ctx, req.AlarmLogListInput) + var reqData = new(model.AlarmLogListInput) + err = gconv.Scan(req, &reqData) + out, err := service.AlarmLog().List(ctx, reqData) res = &alarm.AlarmLogListRes{ AlarmLogListOutput: out, } @@ -29,6 +33,8 @@ func (c *cAlarmLog) List(ctx context.Context, req *alarm.AlarmLogListReq) (res * } func (c *cAlarmLog) Handle(ctx context.Context, req *alarm.AlarmLogHandleReq) (res *alarm.AlarmLogHandleRes, err error) { - err = service.AlarmLog().Handle(ctx, &req.AlarmLogHandleInput) + var reqData = new(model.AlarmLogHandleInput) + err = gconv.Scan(req, &reqData) + err = service.AlarmLog().Handle(ctx, reqData) return } diff --git a/internal/controller/alarm/alarm_rule.go b/internal/controller/alarm/alarm_rule.go index 53cc287..9b96fb3 100644 --- a/internal/controller/alarm/alarm_rule.go +++ b/internal/controller/alarm/alarm_rule.go @@ -2,8 +2,10 @@ package alarm import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/alarm" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/alarm" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var AlarmRule = cAlarmRule{} @@ -11,7 +13,9 @@ var AlarmRule = cAlarmRule{} type cAlarmRule struct{} func (c *cAlarmRule) List(ctx context.Context, req *alarm.AlarmRuleListReq) (res *alarm.AlarmRuleListRes, err error) { - out, err := service.AlarmRule().List(ctx, req.AlarmRuleListInput) + var reqData = new(model.AlarmRuleListInput) + err = gconv.Scan(req, &reqData) + out, err := service.AlarmRule().List(ctx, reqData) res = &alarm.AlarmRuleListRes{ AlarmRuleListOutput: out, } @@ -74,7 +78,7 @@ func (c *cAlarmRule) TriggerType(ctx context.Context, req *alarm.AlarmRuleTrigge } func (c *cAlarmRule) TriggerParam(ctx context.Context, req *alarm.AlarmRuleTriggerParamReq) (res *alarm.AlarmRuleTriggerParamRes, err error) { - out, err := service.AlarmRule().TriggerParam(ctx, req.ProductKey, req.TriggerType) + out, err := service.AlarmRule().TriggerParam(ctx, req.ProductKey, req.TriggerType, req.EventKey) if err != nil || out == nil { return } @@ -82,3 +86,13 @@ func (c *cAlarmRule) TriggerParam(ctx context.Context, req *alarm.AlarmRuleTrigg res.List = out return } + +func (c *cAlarmRule) AddCronRule(ctx context.Context, req *alarm.AlarmCronRuleAddReq) (res *alarm.AlarmCronRuleAddRes, err error) { + err = service.AlarmRule().AddCronRule(ctx, req.AlarmCronRuleAddInput) + return +} + +func (c *cAlarmRule) EditCronRule(ctx context.Context, req *alarm.AlarmCronRuleEditReq) (res *alarm.AlarmCronRuleEditRes, err error) { + err = service.AlarmRule().EditCronRule(ctx, req.AlarmCronRuleEditInput) + return +} diff --git a/internal/controller/analysis/alarm.go b/internal/controller/analysis/alarm.go new file mode 100644 index 0000000..d5d9e3e --- /dev/null +++ b/internal/controller/analysis/alarm.go @@ -0,0 +1,71 @@ +package analysis + +import ( + "context" + "sagooiot/api/v1/analysis" + "sagooiot/internal/service" +) + +var Alarm = cAlarm{} + +type cAlarm struct{} + +// GetDeviceAlarmLevelStats 获取告警级别统计 +func (c *cAlarm) GetDeviceAlarmLevelStats(ctx context.Context, req *analysis.DeviceAlarmLevelCountReq) (res *analysis.DeviceAlarmLevelCountRes, err error) { + data, err := service.AnalysisAlarm().GetAlarmLevelCount(ctx, req.DateType, req.Date) + if err != nil { + return + } + res = &analysis.DeviceAlarmLevelCountRes{ + Data: data, + } + return +} + +// GetDeviceAlarmTotalCount 告警总数统计(当年、当月、当日) +func (c *cAlarm) GetDeviceAlarmTotalCount(ctx context.Context, req *analysis.DeviceAlarmTotalCountReq) (res *analysis.DeviceAlarmTotalCountRes, err error) { + number, err := service.AnalysisAlarm().GetAlarmTotalCount(ctx, req.DateType, req.Date) + if err != nil { + return + } + res = &analysis.DeviceAlarmTotalCountRes{ + Number: number, + } + return +} + +// GetDeviceAlertCountByYearMonth 按年度每月设备告警数统计 +func (c *cAlarm) GetDeviceAlertCountByYearMonth(ctx context.Context, req *analysis.DeviceAlertCountByYearMonthReq) (res *analysis.DeviceAlertCountByYearMonthRes, err error) { + data, err := service.AnalysisAlarm().GetDeviceAlertCountByYearMonth(ctx, req.Year) + if err != nil { + return + } + res = &analysis.DeviceAlertCountByYearMonthRes{ + Data: data, + } + return +} + +// GetDeviceAlertCountByMonthDay 按月度每日设备告警数统计 +func (c *cAlarm) GetDeviceAlertCountByMonthDay(ctx context.Context, req *analysis.DeviceAlertCountByMonthDayReq) (res *analysis.DeviceAlertCountByMonthDayRes, err error) { + data, err := service.AnalysisAlarm().GetDeviceAlertCountByYearMonth(ctx, req.Month) + if err != nil { + return + } + res = &analysis.DeviceAlertCountByMonthDayRes{ + Data: data, + } + return +} + +// GetDeviceAlertCountsByDayHour 按日每小时设备告警数统计 +func (c *cAlarm) GetDeviceAlertCountsByDayHour(ctx context.Context, req *analysis.DeviceAlertCountByDayHourReq) (res *analysis.DeviceAlertCountByDayHourRes, err error) { + data, err := service.AnalysisAlarm().GetDeviceAlertCountByYearMonth(ctx, req.Day) + if err != nil { + return + } + res = &analysis.DeviceAlertCountByDayHourRes{ + Data: data, + } + return +} diff --git a/internal/controller/analysis/device.go b/internal/controller/analysis/device.go new file mode 100644 index 0000000..a628522 --- /dev/null +++ b/internal/controller/analysis/device.go @@ -0,0 +1,71 @@ +package analysis + +import ( + "context" + "sagooiot/api/v1/analysis" + "sagooiot/internal/service" +) + +var Device = cDevice{} + +type cDevice struct{} + +// GetDeviceDataTotalCount 获取设备消息总数 +func (c *cDevice) GetDeviceDataTotalCount(ctx context.Context, req *analysis.DeviceDataTotalCountReq) (res *analysis.DeviceDataTotalCountRes, err error) { + number, err := service.AnalysisDevice().GetDeviceDataTotalCount(ctx, req.DateType) + if err != nil { + return + } + res = &analysis.DeviceDataTotalCountRes{ + Number: number, + } + return +} + +// GetDeviceOnlineOfflineCount 设备在线离线统计, 设备总数,在线数,离线数 +func (c *cDevice) GetDeviceOnlineOfflineCount(ctx context.Context, req *analysis.DeviceOnlineOfflineCountReq) (res *analysis.DeviceOnlineOfflineCountRes, err error) { + data, err := service.AnalysisDevice().GetDeviceOnlineOfflineCount(ctx) + if err != nil { + return + } + res = &analysis.DeviceOnlineOfflineCountRes{ + Data: data, + } + return +} + +// GetDeviceDataCount 设备数据统计,按年、月、日三种类型 +func (c *cDevice) GetDeviceDataCount(ctx context.Context, req *analysis.DeviceDataCountReq) (res *analysis.DeviceDataCountRes, err error) { + data, err := service.AnalysisDevice().GetDeviceDataCountList(ctx, req.DateType) + if err != nil { + return + } + res = &analysis.DeviceDataCountRes{ + Data: data, + } + return +} + +//// GetDeviceDataCountByMonthDay 指定月份1-30日设备消息量统计数据 +//func (c *cDevice) GetDeviceDataCountByMonthDay(ctx context.Context, req *analysis.DeviceDataCountByMonthDayReq) (res *analysis.DeviceDataCountByMonthDayRes, err error) { +// data, err := service.AnalysisDevice().GetCountDeviceDataCountList(ctx, req.Month) +// if err != nil { +// return +// } +// res = &analysis.DeviceDataCountByMonthDayRes{ +// Data: data, +// } +// return +//} +// +//// GetDeviceDataCountByDayHour 按日每小时设备消息统计 +//func (c *cDevice) GetDeviceDataCountByDayHour(ctx context.Context, req *analysis.DeviceDataCountByDayHourReq) (res *analysis.DeviceDataCountByDayHourRes, err error) { +// data, err := service.AnalysisDevice().GetCountDeviceDataCountList(ctx, req.Day) +// if err != nil { +// return +// } +// res = &analysis.DeviceDataCountByDayHourRes{ +// Data: data, +// } +// return +//} diff --git a/internal/controller/analysis/deviceData.go b/internal/controller/analysis/deviceData.go new file mode 100644 index 0000000..de1a78b --- /dev/null +++ b/internal/controller/analysis/deviceData.go @@ -0,0 +1,81 @@ +package analysis + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/analysis" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/general" +) + +var DeviceData = cDeviceData{} + +type cDeviceData struct{} + +// GetDeviceData 获取设备最近的数据 +func (c *cDeviceData) GetDeviceData(ctx context.Context, req *analysis.DeviceDataReq) (res *analysis.DeviceDataRes, err error) { + var reqData model.DeviceDataReq + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + + data, err := service.AnalysisDeviceData().GetDeviceData(ctx, reqData) + if err != nil { + return + } + res = &analysis.DeviceDataRes{ + Data: data, + } + return +} + +// GetDeviceDataForProductByLatest 获取产品下的所有设备最新一条数据 +func (c *cDeviceData) GetDeviceDataForProductByLatest(ctx context.Context, req *analysis.DeviceDataForProductByLatestReq) (res *analysis.DeviceDataForProductByLatestRes, err error) { + data, err := service.AnalysisDeviceData().GetDeviceDataForProductByLatest(ctx, req.ProductKey) + if err != nil { + return + } + res = &analysis.DeviceDataForProductByLatestRes{ + Data: data, + } + return +} + +// GetDeviceDataForTsd 获取设备的时序数据 +func (c *cDeviceData) GetDeviceDataForTsd(ctx context.Context, req *analysis.DeviceDataForTsdReq) (res *analysis.DeviceDataForTsdRes, err error) { + var reqData general.SelectReq + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + + data, err := service.AnalysisDeviceDataTsd().GetDeviceData(ctx, reqData) + if err != nil { + return + } + res = &analysis.DeviceDataForTsdRes{ + Data: data, + } + return + +} + +// GetDeviceAlarmLogData 获取设备告警日志数据 +func (c *cDeviceData) GetDeviceAlarmLogData(ctx context.Context, req *analysis.DeviceAlarmLogDataReq) (res *analysis.DeviceAlarmLogDataRes, err error) { + var reqData = new(general.SelectReq) + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + + data, err := service.AnalysisDeviceData().GetDeviceAlarmLogData(ctx, reqData) + if err != nil { + return + } + res = &analysis.DeviceAlarmLogDataRes{ + Data: data, + } + return +} diff --git a/internal/controller/analysis/product.go b/internal/controller/analysis/product.go new file mode 100644 index 0000000..475c18c --- /dev/null +++ b/internal/controller/analysis/product.go @@ -0,0 +1,38 @@ +package analysis + +import ( + "context" + "sagooiot/api/v1/analysis" + "sagooiot/internal/service" +) + +var Product = cProduct{} + +type cProduct struct{} + +// GetProductCount 获取产品数量统计 +func (c *cProduct) GetProductCount(ctx context.Context, req *analysis.ProductCountReq) (res *analysis.ProductCountRes, err error) { + data, err := service.AnalysisProduct().GetProductCount(ctx) + if err != nil { + return + } + res = &analysis.ProductCountRes{ + Total: data.Total, + Enable: data.Enable, + Disable: data.Disable, + Added: data.Added, + } + return +} + +// GetDeviceCountForProduct 获取属于该产品下的设备数量 +func (c *cProduct) GetDeviceCountForProduct(ctx context.Context, req *analysis.DeviceCountForProductReq) (res *analysis.DeviceCountForProductRes, err error) { + data, err := service.AnalysisProduct().GetDeviceCountForProduct(ctx, req.ProductKey) + if err != nil { + return + } + res = &analysis.DeviceCountForProductRes{ + Number: data, + } + return +} diff --git a/internal/controller/common/base_db_link.go b/internal/controller/common/base_db_link.go deleted file mode 100644 index be8d6e9..0000000 --- a/internal/controller/common/base_db_link.go +++ /dev/null @@ -1,80 +0,0 @@ -package common - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/util/gconv" -) - -var BaseDbLink = cBaseDbLink{} - -type cBaseDbLink struct{} - -// GetList 获取数据源列表 -func (a *cBaseDbLink) GetList(ctx context.Context, req *common.BaseDbLinkDoReq) (res *common.BaseDbLinkDoRes, err error) { - var input *model.BaseDbLinkDoInput - if err = gconv.Scan(req, &input); err != nil { - return - } - - total, out, err := service.BaseDbLink().GetList(ctx, input) - if err != nil { - return - } - res = new(common.BaseDbLinkDoRes) - res.Total = total - res.CurrentPage = req.PageNum - if out != nil { - if err = gconv.Scan(out, &res.Data); err != nil { - return - } - } - return -} - -// AddBaseDbLink 添加数据源 -func (a *cBaseDbLink) AddBaseDbLink(ctx context.Context, req *common.AddBaseDbLinkReq) (res *common.AddBaseDbLinkRes, err error) { - var input *model.AddBaseDbLinkInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.BaseDbLink().Add(ctx, input) - return -} - -// DetailBaseDbLink 获取数据源详情 -func (a *cBaseDbLink) DetailBaseDbLink(ctx context.Context, req *common.DetailBaseDbLinkReq) (res *common.DetailBaseDbLinkRes, err error) { - data, err := service.BaseDbLink().Detail(ctx, req.Id) - if err != nil { - return nil, err - } - if data != nil { - var detailRes *model.DetailBaseDbLinkRes - if err = gconv.Scan(data, &detailRes); err != nil { - return nil, err - } - res = &common.DetailBaseDbLinkRes{ - Data: detailRes, - } - } - return -} - -// EditBaseDbLink 编辑数据源 -func (a *cBaseDbLink) EditBaseDbLink(ctx context.Context, req *common.EditBaseDbLinkReq) (res *common.EditBaseDbLinkRes, err error) { - var input *model.EditBaseDbLinkInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.BaseDbLink().Edit(ctx, input) - return -} - -// DelBaseDbLink 根据ID删除数据源 -func (a *cBaseDbLink) DelBaseDbLink(ctx context.Context, req *common.DelBaseDbLinkReq) (res *common.DelBaseDbLinkRes, err error) { - err = service.BaseDbLink().Del(ctx, req.Id) - return -} diff --git a/internal/controller/common/check_auth.go b/internal/controller/common/check_auth.go new file mode 100644 index 0000000..9352dcc --- /dev/null +++ b/internal/controller/common/check_auth.go @@ -0,0 +1,37 @@ +package common + +import ( + "context" + "sagooiot/api/v1/common" + "sagooiot/internal/service" +) + +var CheckAuth = cCheckAuth{} + +type cCheckAuth struct{} + +// CheckAccessAuth 验证访问权限 +func (c *cCheckAuth) CheckAccessAuth(ctx context.Context, req *common.CheckAccessAuthReq) (res *common.CheckAccessAuthRes, err error) { + isAllow, err := service.CheckAuth().CheckAccessAuth(ctx, req.Address) + if err != nil { + return + } + res = &common.CheckAccessAuthRes{ + IsAllow: isAllow, + } + return +} + +// IsToken 验证token是否正确 +func (c *cSysInfo) IsToken(ctx context.Context, req *common.IsTokenReq) (res *common.IsTokenRes, err error) { + isToken, expiresAt, isAuth, err := service.CheckAuth().IsToken(ctx) + if err != nil { + return + } + res = &common.IsTokenRes{ + IsToken: isToken, + ExpiresAt: expiresAt, + Auth: isAuth, + } + return +} diff --git a/internal/controller/common/city_data.go b/internal/controller/common/city_data.go deleted file mode 100644 index a84080f..0000000 --- a/internal/controller/common/city_data.go +++ /dev/null @@ -1,127 +0,0 @@ -package common - -import ( - "context" - "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var CityData = cCityData{} - -type cCityData struct{} - -// CityTree 获取列表 -func (a *cCityData) CityTree(ctx context.Context, req *common.CityTreeReq) (res *common.CityTreeRes, err error) { - info, err := service.CityData().GetList(ctx, req.Status, req.Name, req.Code) - if err != nil { - return - } - if info != nil { - var dataTree []*model.CityTreeRes - if err = gconv.Scan(info, &dataTree); err != nil { - return - } - treeData, er := GetCityTreeRes(dataTree) - if er != nil { - return - } - - res = &common.CityTreeRes{ - Data: treeData, - } - } - - return -} - -func GetCityTreeRes(heatStationInfo []*model.CityTreeRes) (dataTree []*model.CityTreeRes, err error) { - var parentNodeRes []*model.CityTreeRes - if heatStationInfo != nil { - //获取所有的根节点 - for _, v := range heatStationInfo { - var parentNode *model.CityTreeRes - if v.ParentId == -1 { - if err = gconv.Scan(v, &parentNode); err != nil { - return - } - parentNodeRes = append(parentNodeRes, parentNode) - } - } - } - treeData := GetCityChildrenTree(parentNodeRes, heatStationInfo) - return treeData, nil -} - -func GetCityChildrenTree(parentNodeRes []*model.CityTreeRes, data []*model.CityTreeRes) (dataTree []*model.CityTreeRes) { - //循环所有一级节点 - for k, v := range parentNodeRes { - //查询所有该节点下的所有子节点 - for _, j := range data { - var node *model.CityTreeRes - if j.ParentId == v.Id { - if err := gconv.Scan(j, &node); err != nil { - return - } - parentNodeRes[k].Children = append(parentNodeRes[k].Children, node) - } - } - GetCityChildrenTree(v.Children, data) - } - return parentNodeRes -} - -// AddCity 添加城市 -func (a *cCityData) AddCity(ctx context.Context, req *common.AddCityReq) (res *common.AddCityRes, err error) { - var city *entity.CityData - if err = gconv.Scan(req.AddCityReq, &city); err != nil { - return - } - err = service.CityData().Add(ctx, city) - if err != nil { - return - } - return -} - -// EditCity 编辑城市 -func (a *cCityData) EditCity(ctx context.Context, req *common.EditCityReq) (res *common.EditCityRes, err error) { - var city *entity.CityData - if err = gconv.Scan(req.EditCityReq, &city); err != nil { - return - } - err = service.CityData().Edit(ctx, city) - if err != nil { - return - } - return -} - -// GetCityById 根据ID获取城市 -func (a *cCityData) GetCityById(ctx context.Context, req *common.GetCityByIdReq) (res *common.GetCityByIdRes, err error) { - data, err := service.CityData().GetInfoById(ctx, req.Id) - if err != nil { - return - } - var city *model.CityRes - if data != nil { - if err = gconv.Scan(data, &city); err != nil { - return - } - } - res = &common.GetCityByIdRes{ - Data: city, - } - return -} - -// DelCityById 根据ID删除城市 -func (a *cCityData) DelCityById(ctx context.Context, req *common.DelCityByIdReq) (res *common.DelCityByIdRes, err error) { - err = service.CityData().DelById(ctx, req.Id) - if err != nil { - return - } - return -} diff --git a/internal/controller/common/config_data.go b/internal/controller/common/config_data.go index 6ea17d8..f928d6b 100644 --- a/internal/controller/common/config_data.go +++ b/internal/controller/common/config_data.go @@ -3,9 +3,9 @@ package common import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/service" ) type cConfigData struct{} @@ -76,3 +76,70 @@ func (c *cConfigData) Delete(ctx context.Context, req *common.ConfigDeleteReq) ( err = service.ConfigData().Delete(ctx, req.Ids) return } + +// ConfigGetByKey 根据key获取系统参数 +func (c *cConfigData) ConfigGetByKey(ctx context.Context, req *common.ConfigGetByKeyReq) (res *common.ConfigGetByKeyRes, err error) { + out, err := service.ConfigData().GetByKey(ctx, req.ConfigKey) + if err != nil { + return + } + var data *model.SysConfigRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &common.ConfigGetByKeyRes{ + Data: data, + } + return +} + +// ConfigGetByKeys 根据key数组获取系统参数 +func (c *cConfigData) ConfigGetByKeys(ctx context.Context, req *common.ConfigGetByKeysReq) (res *common.ConfigGetByKeysRes, err error) { + out, err := service.ConfigData().GetConfigByKeys(ctx, req.ConfigKey) + if err != nil { + return + } + var data []*model.SysConfigRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &common.ConfigGetByKeysRes{ + Data: data, + } + return +} + +// GetSysConfigSetting 获取系统配置 +func (c *cConfigData) GetSysConfigSetting(ctx context.Context, req *common.GetSysConfigSettingReq) (res *common.GetSysConfigSettingRes, err error) { + out, err := service.ConfigData().GetSysConfigSetting(ctx, req.Types) + if err != nil { + return + } + var data []*model.SysConfigRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &common.GetSysConfigSettingRes{ + Info: data, + } + return +} + +// EditSysConfigSetting 修改配置 +func (c *cConfigData) EditSysConfigSetting(ctx context.Context, req *common.EditSysConfigSettingReq) (res *common.EditSysConfigSettingRes, err error) { + var input []*model.EditConfigInput + if err = gconv.Scan(req.ConfigInfo, &input); err != nil { + return + } + err = service.ConfigData().EditSysConfigSetting(ctx, input) + if err != nil { + return + } + return +} diff --git a/internal/controller/common/dict_data.go b/internal/controller/common/dict_data.go index d320f02..69fe855 100644 --- a/internal/controller/common/dict_data.go +++ b/internal/controller/common/dict_data.go @@ -3,9 +3,9 @@ package common import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/service" ) type cDictData struct{} diff --git a/internal/controller/common/dict_type.go b/internal/controller/common/dict_type.go index e45e1dd..1bffb71 100644 --- a/internal/controller/common/dict_type.go +++ b/internal/controller/common/dict_type.go @@ -3,9 +3,9 @@ package common import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/service" ) type cDictType struct{} diff --git a/internal/controller/common/sysinfo.go b/internal/controller/common/sysinfo.go index 398cc67..ad1021f 100644 --- a/internal/controller/common/sysinfo.go +++ b/internal/controller/common/sysinfo.go @@ -2,85 +2,25 @@ package common import ( "context" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/version" + "sagooiot/api/v1/common" + "sagooiot/internal/service" ) type cSysInfo struct{} var SysInfo = cSysInfo{} -func (s *cSysInfo) GetSysInfo(ctx context.Context, req *common.GetSysInfoReq) (res *common.GetSysInfoRes, err error) { +// GetSysInfo 系统初始化显示的相关信息 +func (c *cSysInfo) GetSysInfo(ctx context.Context, req *common.GetSysInfoReq) (res *common.GetSysInfoRes, err error) { - cfgSystemName, err := service.ConfigData().GetConfigByKey(ctx, "sys.system.name") - systemName := "沙果IOT" - if cfgSystemName != nil { - systemName = cfgSystemName.ConfigValue - } - - cfgSystemCopyright, err := service.ConfigData().GetConfigByKey(ctx, "sys.system.copyright") - systemCopyright := "Sagoo inc." - if cfgSystemName != nil { - systemCopyright = cfgSystemCopyright.ConfigValue - } - - cfgSystemLogo, err := service.ConfigData().GetConfigByKey(ctx, "sys.system.logo") - systemLogo := "" - if cfgSystemLogo != nil { - systemLogo = cfgSystemLogo.ConfigValue - } - cfgSystemLogoMini, err := service.ConfigData().GetConfigByKey(ctx, "sys.system.logo.mini") - systemLogoMini := "" - if cfgSystemLogoMini != nil { - systemLogoMini = cfgSystemLogoMini.ConfigValue - } - cfgSystemLoginPic, err := service.ConfigData().GetConfigByKey(ctx, "sys.system.login.pic") - systemLoginPic := "" - if cfgSystemLoginPic != nil { - systemLoginPic = cfgSystemLoginPic.ConfigValue - } - - cfgHomePageRoute, err := service.ConfigData().GetConfigByKey(ctx, "homePageRoute") - systemHomePageRoute := "" - if cfgHomePageRoute != nil { - systemHomePageRoute = cfgHomePageRoute.ConfigValue + out, err := service.SysInfo().GetSysInfo(ctx) + if err != nil { + return } - res = &common.GetSysInfoRes{ - "systemName": systemName, - "systemCopyright": systemCopyright, - "systemLogo": systemLogo, - "systemLogoMini": systemLogoMini, - "systemLoginPIC": systemLoginPic, - "buildVersion": version.BuildVersion, - "buildTime": version.BuildTime, - "systemHomePageRoute": systemHomePageRoute, - } + var resData = common.GetSysInfoRes(out) - return -} + res = &resData -// IsToken 验证token是否正确 -func (s *cSysInfo) IsToken(ctx context.Context, req *common.IsTokenReq) (res *common.IsTokenRes, err error) { - authorization := ghttp.RequestFromCtx(ctx).Header.Get("Authorization") - if authorization == "" { - err = gerror.New("请先登录!") - return - } - var isToken = false - var expiresAt int64 - //验证TOKEN是否正确 - data, _ := service.SysToken().ParseToken(ghttp.RequestFromCtx(ctx)) - if data != nil { - isToken = true - expiresAt = data.ExpiresAt - } - res = &common.IsTokenRes{ - IsToken: isToken, - ExpiresAt: expiresAt, - } return } diff --git a/internal/controller/common/upload.go b/internal/controller/common/upload.go index 8e508c4..94d03f5 100644 --- a/internal/controller/common/upload.go +++ b/internal/controller/common/upload.go @@ -4,16 +4,16 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/common" + "sagooiot/internal/consts" + "sagooiot/internal/service" ) type cUpload struct{} var Upload = cUpload{} -//SingleImg 上传单图 +// SingleImg 上传单图 func (c *cUpload) SingleImg(ctx context.Context, req *common.UploadSingleImgReq) (res *common.UploadSingleRes, err error) { r := g.RequestFromCtx(ctx) file := r.GetUploadFile("file") @@ -21,8 +21,7 @@ func (c *cUpload) SingleImg(ctx context.Context, req *common.UploadSingleImgReq) err = gerror.New("上传文件必须") return } - v, _ := g.Cfg().Get(ctx, "upload.default") - response, err := service.Upload().UploadFile(ctx, file, consts.CheckFileTypeImg, v.Int()) + response, err := service.Upload().UploadFile(ctx, file, consts.CheckFileTypeImg, req.Source) if err != nil { return } @@ -33,7 +32,7 @@ func (c *cUpload) SingleImg(ctx context.Context, req *common.UploadSingleImgReq) return } -//MultipleImg 上传多图 +// MultipleImg 上传多图 func (c *cUpload) MultipleImg(ctx context.Context, req *common.UploadMultipleImgReq) (res *common.UploadMultipleRes, err error) { r := g.RequestFromCtx(ctx) files := r.GetUploadFiles("file") @@ -41,8 +40,7 @@ func (c *cUpload) MultipleImg(ctx context.Context, req *common.UploadMultipleImg err = gerror.New("上传文件必须") return } - v, _ := g.Cfg().Get(ctx, "upload.default") - mf, err := service.Upload().UploadFiles(ctx, files, consts.CheckFileTypeImg, v.Int()) + mf, err := service.Upload().UploadFiles(ctx, files, consts.CheckFileTypeImg, req.Source) if err != nil { return } @@ -50,7 +48,7 @@ func (c *cUpload) MultipleImg(ctx context.Context, req *common.UploadMultipleImg return } -//SingleFile 上传单文件 +// SingleFile 上传单文件 func (c *cUpload) SingleFile(ctx context.Context, req *common.UploadSingleFileReq) (res *common.UploadSingleRes, err error) { r := g.RequestFromCtx(ctx) file := r.GetUploadFile("file") @@ -58,8 +56,7 @@ func (c *cUpload) SingleFile(ctx context.Context, req *common.UploadSingleFileRe err = gerror.New("上传文件必须") return } - v, _ := g.Cfg().Get(ctx, "upload.default") - response, err := service.Upload().UploadFile(ctx, file, consts.CheckFileTypeFile, v.Int()) + response, err := service.Upload().UploadFile(ctx, file, consts.CheckFileTypeFile, req.Source) if err != nil { return } @@ -69,7 +66,7 @@ func (c *cUpload) SingleFile(ctx context.Context, req *common.UploadSingleFileRe return } -//MultipleFile 上传多文件 +// MultipleFile 上传多文件 func (c *cUpload) MultipleFile(ctx context.Context, req *common.UploadMultipleFileReq) (res *common.UploadMultipleRes, err error) { r := g.RequestFromCtx(ctx) files := r.GetUploadFiles("file") @@ -77,8 +74,7 @@ func (c *cUpload) MultipleFile(ctx context.Context, req *common.UploadMultipleFi err = gerror.New("上传文件必须") return } - v, _ := g.Cfg().Get(ctx, "upload.default") - mf, err := service.Upload().UploadFiles(ctx, files, consts.CheckFileTypeFile, v.Int()) + mf, err := service.Upload().UploadFiles(ctx, files, consts.CheckFileTypeFile, req.Source) if err != nil { return } diff --git a/internal/controller/envirotronics/env_device_tree.go b/internal/controller/envirotronics/env_device_tree.go new file mode 100644 index 0000000..8c95b80 --- /dev/null +++ b/internal/controller/envirotronics/env_device_tree.go @@ -0,0 +1,22 @@ +package envirotronics + +import ( + "context" + "sagooiot/api/v1/envirotronics" + "sagooiot/internal/service" +) + +var EnvDeviceTree = cEnvDeviceTree{} + +type cEnvDeviceTree struct{} + +func (c *cEnvDeviceTree) Statistic(ctx context.Context, req *envirotronics.EnvDeviceTreeStatisticReq) (res *envirotronics.EnvDeviceTreeStatisticRes, err error) { + out, err := service.EnvDeviceTree().Statistic(ctx, req.EnvDeviceTreeStatisticInput) + if err != nil { + return + } + res = &envirotronics.EnvDeviceTreeStatisticRes{ + EnvDeviceTreeStatisticOutput: out, + } + return +} diff --git a/internal/controller/envirotronics/env_weather.go b/internal/controller/envirotronics/env_weather.go index 89962a1..2585b9e 100644 --- a/internal/controller/envirotronics/env_weather.go +++ b/internal/controller/envirotronics/env_weather.go @@ -3,9 +3,9 @@ package envirotronics import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/envirotronics" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/envirotronics" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var Weather = cWeather{} @@ -51,7 +51,7 @@ func (a *cWeather) GetCityWeatherById(ctx context.Context, req *envirotronics.Ge // GetCityTemperatureById 根据ID获取指定城市的温度图表 func (a *cWeather) GetCityTemperatureById(ctx context.Context, req *envirotronics.GetCityTemperatureByIdReq) (res *envirotronics.GetCityTemperatureByIdRes, err error) { - cityWeatherEchartOut, avgCityWeatherEchartOut, foreCastCityWeatherEchartOut, foreCastAvgCityWeatherEchartOut, err := service.EnvWeather().GetCityTemperatureById(ctx, req.Id, req.Types) + cityWeatherEchartOut, avgCityWeatherEchartOut, foreCastCityWeatherHighEchartOut, foreCastCityWeatherLowEchartOut, err := service.EnvWeather().GetCityTemperatureById(ctx, req.Id, req.Types) if err != nil { return } @@ -67,23 +67,23 @@ func (a *cWeather) GetCityTemperatureById(ctx context.Context, req *envirotronic return } } - var foreCastCityWeatherEchartRes []*model.CityWeatherEchartRes - if foreCastCityWeatherEchartOut != nil { - if err = gconv.Scan(foreCastCityWeatherEchartOut, &foreCastCityWeatherEchartRes); err != nil { + var foreCastCityWeatherHighEchartRes []*model.CityWeatherEchartRes + if foreCastCityWeatherHighEchartOut != nil { + if err = gconv.Scan(foreCastCityWeatherHighEchartOut, &foreCastCityWeatherHighEchartRes); err != nil { return } } - var foreCastAvgCityWeatherEchartRes []*model.CityWeatherEchartRes - if foreCastAvgCityWeatherEchartOut != nil { - if err = gconv.Scan(foreCastAvgCityWeatherEchartOut, &foreCastAvgCityWeatherEchartRes); err != nil { + var foreCastCityWeatherLowEchartRes []*model.CityWeatherEchartRes + if foreCastCityWeatherLowEchartOut != nil { + if err = gconv.Scan(foreCastCityWeatherLowEchartOut, &foreCastCityWeatherLowEchartRes); err != nil { return } } res = &envirotronics.GetCityTemperatureByIdRes{ - Info: cityWeatherEchartRes, - AvgInfo: avgCityWeatherEchartRes, - ForeCastInfo: foreCastCityWeatherEchartRes, - ForeCastAvgInfo: foreCastAvgCityWeatherEchartRes, + Info: cityWeatherEchartRes, + AvgInfo: avgCityWeatherEchartRes, + ForeCastHighInfo: foreCastCityWeatherHighEchartRes, + ForeCastLowInfo: foreCastCityWeatherLowEchartRes, } return } @@ -126,3 +126,31 @@ func (a *cWeather) GetCityWindpowerById(ctx context.Context, req *envirotronics. } return } + +// GetCityWeatherHistoryById 根据ID获取指定城市的天气历史数据 +func (a *cWeather) GetCityWeatherHistoryById(ctx context.Context, req *envirotronics.GetCityWeatherHistoryByIdReq) (res *envirotronics.GetCityWeatherHistoryByIdRes, err error) { + out, err := service.EnvWeather().GetCityWeatherHistoryById(ctx, req.Id, req.DateTime) + if err != nil { + return + } + var cityWeatherHistoryListRes []*model.CityWeatherHistoryListRes + if out != nil { + if err = gconv.Scan(out, &cityWeatherHistoryListRes); err != nil { + return + } + } + + res = &envirotronics.GetCityWeatherHistoryByIdRes{ + Info: cityWeatherHistoryListRes, + } + return +} + +// GetCityWeatherHistoryByIdExport 根据ID获取指定城市的天气历史数据导出 +func (a *cWeather) GetCityWeatherHistoryByIdExport(ctx context.Context, req *envirotronics.GetCityWeatherHistoryByIdExportReq) (res *envirotronics.GetCityWeatherHistoryByIdExportRes, err error) { + err = service.EnvWeather().GetCityWeatherHistoryByIdExport(ctx, req.Id, req.DateTime) + if err != nil { + return + } + return +} diff --git a/internal/controller/network/server.go b/internal/controller/network/server.go index 35b4147..9e529d8 100644 --- a/internal/controller/network/server.go +++ b/internal/controller/network/server.go @@ -4,9 +4,9 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/network" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/network" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var Server = cNetworkServer{} diff --git a/internal/controller/network/tunnel.go b/internal/controller/network/tunnel.go index 4238b7c..016bfbe 100644 --- a/internal/controller/network/tunnel.go +++ b/internal/controller/network/tunnel.go @@ -2,9 +2,9 @@ package network import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/network" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/network" + "sagooiot/internal/model" + "sagooiot/internal/service" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" diff --git a/internal/controller/notice/Log.go b/internal/controller/notice/Log.go index 339e257..f020c4c 100644 --- a/internal/controller/notice/Log.go +++ b/internal/controller/notice/Log.go @@ -2,8 +2,8 @@ package notice import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/notice" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/notice" + "sagooiot/internal/service" ) var NoticeLog = cNoticeNoticeLog{} diff --git a/internal/controller/notice/config.go b/internal/controller/notice/config.go index f26e4a3..4d547e0 100644 --- a/internal/controller/notice/config.go +++ b/internal/controller/notice/config.go @@ -4,17 +4,16 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/gogf/gf/v2/util/guid" - "github.com/sagoo-cloud/sagooiot/api/v1/notice" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/notice" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var NoticeConfig = cNoticeNoticeConfig{} type cNoticeNoticeConfig struct{} -//GetNoticeConfigList 获取列表 +// GetNoticeConfigList 获取列表 func (u *cNoticeNoticeConfig) GetNoticeConfigList(ctx context.Context, req *notice.GetNoticeConfigListReq) (res *notice.GetNoticeConfigListRes, err error) { var reqData = new(model.GetNoticeConfigListInput) err = gconv.Scan(req, &reqData) @@ -26,7 +25,7 @@ func (u *cNoticeNoticeConfig) GetNoticeConfigList(ctx context.Context, req *noti return } -//GetNoticeConfigById 获取指定ID数据 +// GetNoticeConfigById 获取指定ID数据 func (u *cNoticeNoticeConfig) GetNoticeConfigById(ctx context.Context, req *notice.GetNoticeConfigByIdReq) (res *notice.GetNoticeConfigByIdRes, err error) { data, err := service.NoticeConfig().GetNoticeConfigById(ctx, req.Id) res = new(notice.GetNoticeConfigByIdRes) @@ -34,16 +33,15 @@ func (u *cNoticeNoticeConfig) GetNoticeConfigById(ctx context.Context, req *noti return } -//AddNoticeConfig 添加数据 +// AddNoticeConfig 添加数据 func (u *cNoticeNoticeConfig) AddNoticeConfig(ctx context.Context, req *notice.AddNoticeConfigReq) (res *notice.AddNoticeConfigRes, err error) { var data = model.NoticeConfigAddInput{} err = gconv.Scan(req, &data) - data.Id = guid.S() err = service.NoticeConfig().AddNoticeConfig(ctx, data) return } -//EditNoticeConfig 修改数据 +// EditNoticeConfig 修改数据 func (u *cNoticeNoticeConfig) EditNoticeConfig(ctx context.Context, req *notice.EditNoticeConfigReq) (res *notice.EditNoticeConfigRes, err error) { var data = model.NoticeConfigEditInput{} err = gconv.Scan(req, &data) @@ -54,7 +52,7 @@ func (u *cNoticeNoticeConfig) EditNoticeConfig(ctx context.Context, req *notice. return } -//DeleteNoticeConfig 删除数据 +// DeleteNoticeConfig 删除数据 func (u *cNoticeNoticeConfig) DeleteNoticeConfig(ctx context.Context, req *notice.DeleteNoticeConfigReq) (res *notice.DeleteNoticeConfigRes, err error) { if len(req.Ids) == 0 { err = gerror.New("ID参数错误") diff --git a/internal/controller/notice/info.go b/internal/controller/notice/info.go index 4006e37..158cd35 100644 --- a/internal/controller/notice/info.go +++ b/internal/controller/notice/info.go @@ -4,16 +4,16 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/notice" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/notice" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var NoticeInfo = cNoticeNoticeInfo{} type cNoticeNoticeInfo struct{} -//GetNoticeInfoList 获取列表 +// GetNoticeInfoList 获取列表 func (u *cNoticeNoticeInfo) GetNoticeInfoList(ctx context.Context, req *notice.GetNoticeInfoListReq) (res *notice.GetNoticeInfoListRes, err error) { var reqData = new(model.GetNoticeInfoListInput) if err = gconv.Scan(req, &reqData); err != nil { @@ -29,7 +29,7 @@ func (u *cNoticeNoticeInfo) GetNoticeInfoList(ctx context.Context, req *notice.G return } -//GetNoticeInfoById 获取指定ID数据 +// GetNoticeInfoById 获取指定ID数据 func (u *cNoticeNoticeInfo) GetNoticeInfoById(ctx context.Context, req *notice.GetNoticeInfoByIdReq) (res *notice.GetNoticeInfoByIdRes, err error) { data, err := service.NoticeInfo().GetNoticeInfoById(ctx, req.Id) res = new(notice.GetNoticeInfoByIdRes) @@ -37,7 +37,7 @@ func (u *cNoticeNoticeInfo) GetNoticeInfoById(ctx context.Context, req *notice.G return } -//AddNoticeInfo 添加数据 +// AddNoticeInfo 添加数据 func (u *cNoticeNoticeInfo) AddNoticeInfo(ctx context.Context, req *notice.AddNoticeInfoReq) (res *notice.AddNoticeInfoRes, err error) { var data = model.NoticeInfoAddInput{} if err = gconv.Scan(req, &data); err != nil { @@ -47,7 +47,7 @@ func (u *cNoticeNoticeInfo) AddNoticeInfo(ctx context.Context, req *notice.AddNo return } -//EditNoticeInfo 修改数据 +// EditNoticeInfo 修改数据 func (u *cNoticeNoticeInfo) EditNoticeInfo(ctx context.Context, req *notice.EditNoticeInfoReq) (res *notice.EditNoticeInfoRes, err error) { var data = model.NoticeInfoEditInput{} if err = gconv.Scan(req, &data); err != nil { @@ -57,7 +57,7 @@ func (u *cNoticeNoticeInfo) EditNoticeInfo(ctx context.Context, req *notice.Edit return } -//DeleteNoticeInfo 删除数据 +// DeleteNoticeInfo 删除数据 func (u *cNoticeNoticeInfo) DeleteNoticeInfo(ctx context.Context, req *notice.DeleteNoticeInfoReq) (res *notice.DeleteNoticeInfoRes, err error) { if len(req.Ids) == 0 { err = gerror.New("ID参数错误") diff --git a/internal/controller/notice/template.go b/internal/controller/notice/template.go index 008048d..6266f81 100644 --- a/internal/controller/notice/template.go +++ b/internal/controller/notice/template.go @@ -5,16 +5,16 @@ import ( "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/guid" - "github.com/sagoo-cloud/sagooiot/api/v1/notice" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/notice" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var NoticeTemplate = cNoticeNoticeTemplate{} type cNoticeNoticeTemplate struct{} -//GetNoticeTemplateList 获取列表 +// GetNoticeTemplateList 获取列表 func (u *cNoticeNoticeTemplate) GetNoticeTemplateList(ctx context.Context, req *notice.GetNoticeTemplateListReq) (res *notice.GetNoticeTemplateListRes, err error) { var reqData = new(model.GetNoticeTemplateListInput) if err = gconv.Scan(req, &reqData); err != nil { @@ -28,7 +28,7 @@ func (u *cNoticeNoticeTemplate) GetNoticeTemplateList(ctx context.Context, req * return } -//GetNoticeTemplateById 获取指定ID数据 +// GetNoticeTemplateById 获取指定ID数据 func (u *cNoticeNoticeTemplate) GetNoticeTemplateById(ctx context.Context, req *notice.GetNoticeTemplateByIdReq) (res *notice.GetNoticeTemplateByIdRes, err error) { data, err := service.NoticeTemplate().GetNoticeTemplateById(ctx, req.Id) res = new(notice.GetNoticeTemplateByIdRes) @@ -36,7 +36,7 @@ func (u *cNoticeNoticeTemplate) GetNoticeTemplateById(ctx context.Context, req * return } -//GetNoticeTemplateByConfigId 获取指定ConfigID数据 +// GetNoticeTemplateByConfigId 获取指定ConfigID数据 func (u *cNoticeNoticeTemplate) GetNoticeTemplateByConfigId(ctx context.Context, req *notice.GetNoticeTemplateByConfigIdReq) (res *notice.GetNoticeTemplateByConfigIdRes, err error) { data, err := service.NoticeTemplate().GetNoticeTemplateByConfigId(ctx, req.ConfigId) res = new(notice.GetNoticeTemplateByConfigIdRes) @@ -47,7 +47,7 @@ func (u *cNoticeNoticeTemplate) GetNoticeTemplateByConfigId(ctx context.Context, return } -//AddNoticeTemplate 添加数据 +// AddNoticeTemplate 添加数据 func (u *cNoticeNoticeTemplate) AddNoticeTemplate(ctx context.Context, req *notice.AddNoticeTemplateReq) (res *notice.AddNoticeTemplateRes, err error) { var data = model.NoticeTemplateAddInput{} if err = gconv.Scan(req, &data); err != nil { @@ -58,7 +58,7 @@ func (u *cNoticeNoticeTemplate) AddNoticeTemplate(ctx context.Context, req *noti return } -//EditNoticeTemplate 修改数据 +// EditNoticeTemplate 修改数据 func (u *cNoticeNoticeTemplate) EditNoticeTemplate(ctx context.Context, req *notice.EditNoticeTemplateReq) (res *notice.EditNoticeTemplateRes, err error) { var data = model.NoticeTemplateEditInput{} if err = gconv.Scan(req, &data); err != nil { @@ -69,7 +69,7 @@ func (u *cNoticeNoticeTemplate) EditNoticeTemplate(ctx context.Context, req *not return } -//SaveNoticeTemplate 直接更新数据 +// SaveNoticeTemplate 直接更新数据 func (u *cNoticeNoticeTemplate) SaveNoticeTemplate(ctx context.Context, req *notice.SaveNoticeTemplateReq) (res *notice.SaveNoticeTemplateRes, err error) { var data = model.NoticeTemplateAddInput{} if err = gconv.Scan(req, &data); err != nil { @@ -82,7 +82,7 @@ func (u *cNoticeNoticeTemplate) SaveNoticeTemplate(ctx context.Context, req *not return } -//DeleteNoticeTemplate 删除数据 +// DeleteNoticeTemplate 删除数据 func (u *cNoticeNoticeTemplate) DeleteNoticeTemplate(ctx context.Context, req *notice.DeleteNoticeTemplateReq) (res *notice.DeleteNoticeTemplateRes, err error) { if len(req.Ids) == 0 { err = gerror.New("ID参数错误") diff --git a/internal/controller/product/category.go b/internal/controller/product/category.go index cc45afe..19f7000 100644 --- a/internal/controller/product/category.go +++ b/internal/controller/product/category.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var Category = cCategory{} diff --git a/internal/controller/product/device.go b/internal/controller/product/device.go index f051cd2..3b7ea8d 100644 --- a/internal/controller/product/device.go +++ b/internal/controller/product/device.go @@ -2,24 +2,18 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/product" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var Device = cDevice{} type cDevice struct{} -func (c *cDevice) Get(ctx context.Context, req *product.GetDeviceReq) (res *product.GetDeviceRes, err error) { - p, err := service.DevDevice().Get(ctx, req.Key) - res = &product.GetDeviceRes{ - Data: p, - } - return -} - func (c *cDevice) Detail(ctx context.Context, req *product.DetailDeviceReq) (res *product.DetailDeviceRes, err error) { - p, err := service.DevDevice().Detail(ctx, req.Id) + p, err := service.DevDevice().Detail(ctx, req.DeviceKey) res = &product.DetailDeviceRes{ Data: p, } @@ -35,7 +29,7 @@ func (c *cDevice) ListForPage(ctx context.Context, req *product.ListDeviceForPag } func (c *cDevice) List(ctx context.Context, req *product.ListDeviceReq) (res *product.ListDeviceRes, err error) { - out, err := service.DevDevice().List(ctx, req.ListDeviceInput) + out, err := service.DevDevice().List(ctx, req.ProductKey, req.KeyWord) res = &product.ListDeviceRes{ Device: out, } @@ -52,33 +46,28 @@ func (c *cDevice) Edit(ctx context.Context, req *product.EditDeviceReq) (res *pr return } -func (c *cDevice) Del(ctx context.Context, req *product.DelDeviceReq) (res *product.DelDeviceRes, err error) { - err = service.DevDevice().Del(ctx, req.Ids) - return -} - -func (c *cDevice) Deploy(ctx context.Context, req *product.DeployDeviceReq) (res *product.DeployDeviceRes, err error) { - err = service.DevDevice().Deploy(ctx, req.Id) +func (c *cDevice) UpdateExtend(ctx context.Context, req *product.UpdateDeviceExtendReq) (res *product.UpdateDeviceExtendRes, err error) { + err = service.DevDevice().UpdateExtend(ctx, req.DeviceExtendInput) return } -func (c *cDevice) Undeploy(ctx context.Context, req *product.UndeployDeviceReq) (res *product.UndeployDeviceRes, err error) { - err = service.DevDevice().Undeploy(ctx, req.Id) +func (c *cDevice) Del(ctx context.Context, req *product.DelDeviceReq) (res *product.DelDeviceRes, err error) { + err = service.DevDevice().Del(ctx, req.Keys) return } -func (c *cDevice) Online(ctx context.Context, req *product.OnlineDeviceReq) (res *product.OnlineDeviceRes, err error) { - err = service.DevDevice().Online(ctx, req.Key) +func (c *cDevice) Deploy(ctx context.Context, req *product.DeployDeviceReq) (res *product.DeployDeviceRes, err error) { + err = service.DevDevice().Deploy(ctx, req.DeviceKey) return } -func (c *cDevice) Offline(ctx context.Context, req *product.OfflineDeviceReq) (res *product.OfflineDeviceRes, err error) { - err = service.DevDevice().Offline(ctx, req.Key) +func (c *cDevice) Undeploy(ctx context.Context, req *product.UndeployDeviceReq) (res *product.UndeployDeviceRes, err error) { + err = service.DevDevice().Undeploy(ctx, req.DeviceKey) return } func (c *cDevice) RunStatus(ctx context.Context, req *product.DeviceRunStatusReq) (res *product.DeviceRunStatusRes, err error) { - out, err := service.DevDevice().RunStatus(ctx, req.Id) + out, err := service.DevDevice().RunStatus(ctx, req.DeviceKey) if err != nil { return } @@ -88,6 +77,17 @@ func (c *cDevice) RunStatus(ctx context.Context, req *product.DeviceRunStatusReq return } +func (c *cDevice) GetLatestProperty(ctx context.Context, req *product.DeviceGetLatestPropertyReq) (res *product.DeviceGetLatestPropertyRes, err error) { + list, err := service.DevDevice().GetLatestProperty(ctx, req.DeviceKey) + if err != nil { + return + } + res = &product.DeviceGetLatestPropertyRes{ + List: list, + } + return +} + func (c *cDevice) GetProperty(ctx context.Context, req *product.DeviceGetPropertyReq) (res *product.DeviceGetPropertyRes, err error) { out, err := service.DevDevice().GetProperty(ctx, req.DeviceGetPropertyInput) res = &product.DeviceGetPropertyRes{ @@ -97,23 +97,63 @@ func (c *cDevice) GetProperty(ctx context.Context, req *product.DeviceGetPropert } func (c *cDevice) GetPropertyList(ctx context.Context, req *product.DeviceGetPropertyListReq) (res *product.DeviceGetPropertyListRes, err error) { - out, err := service.DevDevice().GetPropertyList(ctx, req.DeviceGetPropertyListInput) + var input *model.DeviceGetPropertyListInput + if err = gconv.Scan(req, &input); err != nil { + return + } + out, err := service.DevDevice().GetPropertyList(ctx, input) res = &product.DeviceGetPropertyListRes{ DeviceGetPropertyListOutput: out, } return } -func (c *cDevice) Statistics(ctx context.Context, req *product.DeviceStatisticsReq) (res product.DeviceStatisticsRes, err error) { - res.DeviceTotal, err = service.DevDevice().Total(ctx) +func (c *cDevice) BindSubDevice(ctx context.Context, req *product.DeviceBindReq) (res *product.DeviceBindRes, err error) { + err = service.DevDevice().BindSubDevice(ctx, req.DeviceBindInput) return } -func (c *cDevice) StatisticsForMonths(ctx context.Context, req *product.DeviceStatisticsForMonthsReq) (res product.DeviceStatisticsForMonthsRes, err error) { - res.MsgTotal, err = service.DevDevice().TotalForMonths(ctx) - if err != nil { - return +func (c *cDevice) UnBindSubDevice(ctx context.Context, req *product.DeviceUnBindReq) (res *product.DeviceUnBindRes, err error) { + err = service.DevDevice().UnBindSubDevice(ctx, req.DeviceBindInput) + return +} + +func (c *cDevice) BindList(ctx context.Context, req *product.BindListReq) (res *product.BindListRes, err error) { + out, err := service.DevDevice().BindList(ctx, req.DeviceBindListInput) + res = &product.BindListRes{ + DeviceBindListOutput: out, + } + return +} + +func (c *cDevice) ListForSub(ctx context.Context, req *product.ListForSubReq) (res *product.ListForSubRes, err error) { + out, err := service.DevDevice().ListForSub(ctx, req.ListForSubInput) + res = &product.ListForSubRes{ + ListDeviceForPageOutput: out, + } + return +} + +func (c *cDevice) DelSub(ctx context.Context, req *product.DelSubDeviceReq) (res *product.DelSubDeviceRes, err error) { + err = service.DevDevice().DelSub(ctx, req.DeviceKey) + return +} + +func (c *cDevice) ImportDevices(ctx context.Context, req *product.ImportDevicesReq) (res product.ImportDevicesRes, err error) { + return service.DevDevice().ImportDevices(ctx, req) +} + +func (c *cDevice) ExportDevices(ctx context.Context, req *product.ExportDevicesReq) (res product.ExportDevicesRes, err error) { + return service.DevDevice().ExportDevices(ctx, req) +} +func (c *cDevice) SetDevicesStatus(ctx context.Context, req *product.SetDeviceStatusReq) (res product.SetDeviceStatusRes, err error) { + return service.DevDevice().SetDevicesStatus(ctx, req) +} + +func (c *cDevice) GetDeviceDataList(ctx context.Context, req *product.DeviceDataListReq) (res *product.DeviceDataListRes, err error) { + out, err := service.DevDevice().GetDeviceDataList(ctx, req.DeviceDataListInput) + res = &product.DeviceDataListRes{ + DeviceDataListOutput: out, } - res.AlarmTotal, err = service.DevDevice().AlarmTotalForMonths(ctx) return } diff --git a/internal/controller/product/device_function.go b/internal/controller/product/device_function.go new file mode 100644 index 0000000..6b0491b --- /dev/null +++ b/internal/controller/product/device_function.go @@ -0,0 +1,20 @@ +package product + +import ( + "context" + "sagooiot/api/v1/product" + "sagooiot/internal/service" +) + +var DeviceFunction = cDeviceFunction{} + +type cDeviceFunction struct{} + +// Do 执行设备功能 +func (c *cDeviceFunction) Do(ctx context.Context, req *product.DeviceFunctionReq) (res *product.DeviceFunctionRes, err error) { + out, err := service.DevDeviceFunction().Do(ctx, req.DeviceFunctionInput) + res = &product.DeviceFunctionRes{ + DeviceFunctionOutput: out, + } + return +} diff --git a/internal/controller/product/device_log.go b/internal/controller/product/device_log.go index b4bc10e..3f1b073 100644 --- a/internal/controller/product/device_log.go +++ b/internal/controller/product/device_log.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var DeviceLog = cDeviceLog{} diff --git a/internal/controller/product/device_property.go b/internal/controller/product/device_property.go new file mode 100644 index 0000000..1fce7cf --- /dev/null +++ b/internal/controller/product/device_property.go @@ -0,0 +1,20 @@ +package product + +import ( + "context" + "sagooiot/api/v1/product" + "sagooiot/internal/service" +) + +var DeviceProperty = cDeviceProperty{} + +type cDeviceProperty struct{} + +// Set 设备属性设置 +func (c *cDeviceProperty) Set(ctx context.Context, req *product.DevicePropertyReq) (res *product.DevicePropertyRes, err error) { + out, err := service.DevDeviceProperty().Set(ctx, req.DevicePropertyInput) + res = &product.DevicePropertyRes{ + DevicePropertyOutput: out, + } + return +} diff --git a/internal/controller/product/device_tag.go b/internal/controller/product/device_tag.go index 984f368..f138c04 100644 --- a/internal/controller/product/device_tag.go +++ b/internal/controller/product/device_tag.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var DeviceTag = cDeviceTag{} diff --git a/internal/controller/product/device_tree.go b/internal/controller/product/device_tree.go new file mode 100644 index 0000000..eb91e43 --- /dev/null +++ b/internal/controller/product/device_tree.go @@ -0,0 +1,53 @@ +package product + +import ( + "context" + "sagooiot/api/v1/product" + "sagooiot/internal/service" +) + +var DeviceTree = cDeviceTree{} + +type cDeviceTree struct{} + +func (c *cDeviceTree) List(ctx context.Context, req *product.DeviceTreeListReq) (res *product.DeviceTreeListRes, err error) { + list, err := service.DevDeviceTree().List(ctx) + if err != nil || len(list) == 0 { + return + } + res = &product.DeviceTreeListRes{ + List: list, + } + return +} + +func (c *cDeviceTree) Change(ctx context.Context, req *product.DeviceTreeChangeReq) (res *product.DeviceTreeChangeRes, err error) { + err = service.DevDeviceTree().Change(ctx, req.InfoId, req.ParentInfoId) + return +} + +func (c *cDeviceTree) Detail(ctx context.Context, req *product.DetailDeviceTreeInfoReq) (res *product.DetailDeviceTreeInfoRes, err error) { + out, err := service.DevDeviceTree().Detail(ctx, req.InfoId) + if err != nil || out == nil { + return + } + res = &product.DetailDeviceTreeInfoRes{ + Data: out, + } + return +} + +func (c *cDeviceTree) Add(ctx context.Context, req *product.AddDeviceTreeInfoReq) (res *product.AddDeviceTreeInfoRes, err error) { + err = service.DevDeviceTree().Add(ctx, req.AddDeviceTreeInfoInput) + return +} + +func (c *cDeviceTree) Edit(ctx context.Context, req *product.EditDeviceTreeInfoReq) (res *product.EditDeviceTreeInfoRes, err error) { + err = service.DevDeviceTree().Edit(ctx, req.EditDeviceTreeInfoInput) + return +} + +func (c *cDeviceTree) Del(ctx context.Context, req *product.DelDeviceTreeInfoReq) (res *product.DelDeviceTreeInfoRes, err error) { + err = service.DevDeviceTree().Del(ctx, req.Id) + return +} diff --git a/internal/controller/product/product.go b/internal/controller/product/product.go index 3cbb2e6..5f354d0 100644 --- a/internal/controller/product/product.go +++ b/internal/controller/product/product.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" @@ -14,16 +14,28 @@ var Product = cProduct{} type cProduct struct{} -func (c *cProduct) Get(ctx context.Context, req *product.GetProductReq) (res *product.GetProductRes, err error) { - p, err := service.DevProduct().Get(ctx, req.Key) - res = &product.GetProductRes{ - Data: p, - } - return -} +//func (c *cProduct) Get(ctx context.Context, req *product.GetProductReq) (res *product.GetProductRes, err error) { +// p, err := service.DevProduct().Get(ctx, req.Key) +// // 获取产品的设备数量 +// totals, err := service.DevDevice().TotalByProductId(ctx, []uint{p.Id}) +// if err != nil { +// return +// } +// p.DeviceTotal = totals[p.Id] +// res = &product.GetProductRes{ +// Data: p, +// } +// return +//} func (c *cProduct) Detail(ctx context.Context, req *product.DetailProductReq) (res *product.DetailProductRes, err error) { - p, err := service.DevProduct().Detail(ctx, req.Id) + p, err := service.DevProduct().Detail(ctx, req.ProductKey) + // 获取产品的设备数量 + totals, err := service.DevDevice().TotalByProductKey(ctx, []string{p.Key}) + if err != nil { + return + } + p.DeviceTotal = totals[p.Key] res = &product.DetailProductRes{ Data: p, } @@ -56,18 +68,23 @@ func (c *cProduct) Edit(ctx context.Context, req *product.EditProductReq) (res * return } +func (c *cProduct) UpdateExtend(ctx context.Context, req *product.UpdateExtendReq) (res *product.UpdateExtendRes, err error) { + err = service.DevProduct().UpdateExtend(ctx, req.ExtendInput) + return +} + func (c *cProduct) Del(ctx context.Context, req *product.DelProductReq) (res *product.DelProductRes, err error) { - err = service.DevProduct().Del(ctx, req.Ids) + err = service.DevProduct().Del(ctx, req.Keys) return } func (c *cProduct) Deploy(ctx context.Context, req *product.DeployProductReq) (res *product.DeployProductRes, err error) { - err = service.DevProduct().Deploy(ctx, req.Id) + err = service.DevProduct().Deploy(ctx, req.ProductKey) return } func (c *cProduct) Undeploy(ctx context.Context, req *product.UndeployProductReq) (res *product.UndeployProductRes, err error) { - err = service.DevProduct().Undeploy(ctx, req.Id) + err = service.DevProduct().Undeploy(ctx, req.ProductKey) return } @@ -90,3 +107,26 @@ func (c *cProduct) UploadIcon(ctx context.Context, req *product.UploadIconReq) ( return } + +func (c *cProduct) ListForSub(ctx context.Context, req *product.ListForSubProductReq) (res *product.ListForSubProductRes, err error) { + list, err := service.DevProduct().ListForSub(ctx) + res = &product.ListForSubProductRes{ + Product: list, + } + return +} + +// UpdateScriptInfo 脚本更新 +func (c *cProduct) UpdateScriptInfo(ctx context.Context, req *product.UpdateScriptInfoReq) (res *product.UpdateScriptInfoRes, err error) { + err = service.DevProduct().UpdateScriptInfo(ctx, req.ScriptInfoInput) + return +} + +// ConnectIntro 获取设备接入信息 +func (c *cProduct) ConnectIntro(ctx context.Context, req *product.ConnectIntroReq) (res *product.ConnectIntroRes, err error) { + data, err := service.DevProduct().ConnectIntro(ctx, req.ProductKey) + res = &product.ConnectIntroRes{ + Data: data, + } + return +} diff --git a/internal/controller/product/protocol.go b/internal/controller/product/protocol.go deleted file mode 100644 index c5d1d07..0000000 --- a/internal/controller/product/protocol.go +++ /dev/null @@ -1,35 +0,0 @@ -package product - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/model" -) - -var Protocol = cProtocol{} - -type cProtocol struct{} - -// ListMessageProtocol 消息协议列表 -func (c *cProtocol) ListMessageProtocol(ctx context.Context, req *product.ListMessageProtocolReq) (res *product.ListMessageProtocolRes, err error) { - res = &product.ListMessageProtocolRes{ - Data: []*model.MessageProtocolRes{ - {Key: "modbus", Name: "modbus"}, - }, - } - return -} - -// ListTrunsportProtocol 传输协议列表 -func (c *cProtocol) ListTrunsportProtocol(ctx context.Context, req *product.ListTrunsportProtocolReq) (res *product.ListTrunsportProtocolRes, err error) { - res = &product.ListTrunsportProtocolRes{ - Data: []*model.TrunsportProtocolRes{ - {Key: "tcp-server", Name: "tcp服务端"}, - {Key: "tcp-client", Name: "tcp客户端"}, - {Key: "udp-server", Name: "udp服务端"}, - {Key: "udp-client", Name: "udp客户端"}, - {Key: "serial", Name: "serial"}, - }, - } - return -} diff --git a/internal/controller/product/tsl_data_type.go b/internal/controller/product/tsl_data_type.go index 0c88a00..5596289 100644 --- a/internal/controller/product/tsl_data_type.go +++ b/internal/controller/product/tsl_data_type.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var TSLDataType = cTSLDataType{} diff --git a/internal/controller/product/tsl_event.go b/internal/controller/product/tsl_event.go index 294d119..a217500 100644 --- a/internal/controller/product/tsl_event.go +++ b/internal/controller/product/tsl_event.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var TSLEvent = cTSLEvent{} @@ -18,13 +18,21 @@ func (c *cTSLEvent) ListEvent(ctx context.Context, req *product.ListTSLEventReq) return } +func (c *cTSLFunction) AllEvent(ctx context.Context, req *product.AllTSLEventReq) (res *product.AllTSLEventRes, err error) { + list, err := service.DevTSLEvent().AllEvent(ctx, req.ProductKey) + res = &product.AllTSLEventRes{ + Data: list, + } + return +} + func (c *cTSLEvent) AddEvent(ctx context.Context, req *product.AddTSLEventReq) (res *product.AddTSLEventRes, err error) { - err = service.DevTSLEvent().AddEvent(ctx, req.TSLEventInput) + err = service.DevTSLEvent().AddEvent(ctx, req.TSLEventAddInput) return } func (c *cTSLEvent) EditEvent(ctx context.Context, req *product.EditTSLEventReq) (res *product.EditTSLEventRes, err error) { - err = service.DevTSLEvent().EditEvent(ctx, req.TSLEventInput) + err = service.DevTSLEvent().EditEvent(ctx, req.TSLEventAddInput) return } diff --git a/internal/controller/product/tsl_function.go b/internal/controller/product/tsl_function.go index 40bd2f1..cf295ac 100644 --- a/internal/controller/product/tsl_function.go +++ b/internal/controller/product/tsl_function.go @@ -2,8 +2,10 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/product" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var TSLFunction = cTSLFunction{} @@ -11,7 +13,12 @@ var TSLFunction = cTSLFunction{} type cTSLFunction struct{} func (c *cTSLFunction) ListFunction(ctx context.Context, req *product.ListTSLFunctionReq) (res *product.ListTSLFunctionRes, err error) { - out, err := service.DevTSLFunction().ListFunction(ctx, req.ListTSLFunctionInput) + var reqData = new(model.ListTSLFunctionInput) + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + out, err := service.DevTSLFunction().ListFunction(ctx, reqData) res = &product.ListTSLFunctionRes{ ListTSLFunctionOutput: out, } @@ -19,7 +26,7 @@ func (c *cTSLFunction) ListFunction(ctx context.Context, req *product.ListTSLFun } func (c *cTSLFunction) AllFunction(ctx context.Context, req *product.AllTSLFunctionReq) (res *product.AllTSLFunctionRes, err error) { - list, err := service.DevTSLFunction().AllFunction(ctx, req.Key, req.InputsValueTypes) + list, err := service.DevTSLFunction().AllFunction(ctx, req.ProductKey, req.InputsValueTypes) res = &product.AllTSLFunctionRes{ Data: list, } diff --git a/internal/controller/product/tsl_import.go b/internal/controller/product/tsl_import.go new file mode 100644 index 0000000..3138a9a --- /dev/null +++ b/internal/controller/product/tsl_import.go @@ -0,0 +1,31 @@ +package product + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/api/v1/product" + "sagooiot/internal/service" +) + +var TSLImport = cTSLImport{} + +type cTSLImport struct{} + +// ExportTSL 导出物模型 +func (c *cTSLImport) ExportTSL(ctx context.Context, req *product.ExportTSLReq) (res *product.ExportTSLRes, err error) { + g.Log().Debug(ctx, "====导出======", req) + err = service.DevTSLImport().Export(ctx, req.ProductKey) + return +} + +// ImportTSL 导出物模型 +func (c *cTSLImport) ImportTSL(ctx context.Context, req *product.ImportTSLReq) (res *product.ImportTSLRes, err error) { + g.Log().Debug(ctx, "====导入======", req) + if req.File == nil { + err = gerror.New("上传文件必须") + return + } + err = service.DevTSLImport().Import(ctx, req.ProductKey, req.File) + return +} diff --git a/internal/controller/product/tsl_property.go b/internal/controller/product/tsl_property.go index 217c054..5f9081c 100644 --- a/internal/controller/product/tsl_property.go +++ b/internal/controller/product/tsl_property.go @@ -2,8 +2,10 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/product" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var TSLProperty = cTSLProperty{} @@ -11,7 +13,12 @@ var TSLProperty = cTSLProperty{} type cTSLProperty struct{} func (c *cTSLProperty) ListProperty(ctx context.Context, req *product.ListTSLPropertyReq) (res *product.ListTSLPropertyRes, err error) { - out, err := service.DevTSLProperty().ListProperty(ctx, req.ListTSLPropertyInput) + var reqData = new(model.ListTSLPropertyInput) + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + out, err := service.DevTSLProperty().ListProperty(ctx, reqData) res = &product.ListTSLPropertyRes{ ListTSLPropertyOutput: out, } @@ -19,7 +26,7 @@ func (c *cTSLProperty) ListProperty(ctx context.Context, req *product.ListTSLPro } func (c *cTSLProperty) AllProperty(ctx context.Context, req *product.AllTSLPropertyReq) (res *product.AllTSLPropertyRes, err error) { - list, err := service.DevTSLProperty().AllProperty(ctx, req.Key) + list, err := service.DevTSLProperty().AllProperty(ctx, req.ProductKey) res = &product.AllTSLPropertyRes{ Data: list, } @@ -37,6 +44,11 @@ func (c *cTSLProperty) EditProperty(ctx context.Context, req *product.EditTSLPro } func (c *cTSLProperty) DelProperty(ctx context.Context, req *product.DelTSLPropertyReq) (res *product.DelTSLPropertyRes, err error) { - err = service.DevTSLProperty().DelProperty(ctx, req.DelTSLPropertyInput) + var reqData = new(model.DelTSLPropertyInput) + err = gconv.Scan(req, &reqData) + if err != nil { + return nil, err + } + err = service.DevTSLProperty().DelProperty(ctx, reqData) return } diff --git a/internal/controller/product/tsl_tag.go b/internal/controller/product/tsl_tag.go index 5367489..5c9d178 100644 --- a/internal/controller/product/tsl_tag.go +++ b/internal/controller/product/tsl_tag.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/product" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/product" + "sagooiot/internal/service" ) var TSLTag = cTSLTag{} diff --git a/internal/controller/source/node.go b/internal/controller/source/node.go deleted file mode 100644 index 682ff2f..0000000 --- a/internal/controller/source/node.go +++ /dev/null @@ -1,38 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var DataNode = cDataNode{} - -type cDataNode struct{} - -// 添加数据节点 -func (c *cDataNode) Add(ctx context.Context, req *source.DataNodeAddReq) (res *source.DataNodeAddRes, err error) { - err = service.DataNode().Add(ctx, req.DataNodeAddInput) - return -} - -// 编辑数据节点 -func (c *cDataNode) Edit(ctx context.Context, req *source.DataNodeEditReq) (res *source.DataNodeEditRes, err error) { - err = service.DataNode().Edit(ctx, req.DataNodeEditInput) - return -} - -// 删除数据节点 -func (c *cDataNode) Del(ctx context.Context, req *source.DataNodeDelReq) (res *source.DataNodeDelRes, err error) { - err = service.DataNode().Del(ctx, req.NodeId) - return -} - -// 数据节点列表 -func (c *cDataNode) List(ctx context.Context, req *source.DataNodeListReq) (res *source.DataNodeListRes, err error) { - list, err := service.DataNode().List(ctx, req.SourceId) - res = &source.DataNodeListRes{ - List: list, - } - return -} diff --git a/internal/controller/source/source.go b/internal/controller/source/source.go deleted file mode 100644 index 7f962d3..0000000 --- a/internal/controller/source/source.go +++ /dev/null @@ -1,94 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var DataSource = cDataSource{} - -type cDataSource struct{} - -// 添加 api 数据源 -func (c *cDataSource) Add(ctx context.Context, req *source.DataSourceApiAddReq) (res *source.DataSourceApiAddRes, err error) { - _, err = service.DataSource().Add(ctx, req.DataSourceApiAddInput) - return -} - -// 编辑 api 数据源 -func (c *cDataSource) Edit(ctx context.Context, req *source.DataSourceApiEditReq) (res *source.DataSourceApiEditRes, err error) { - err = service.DataSource().Edit(ctx, req.DataSourceApiEditInput) - return -} - -// 获取 api 数据 -func (c *cDataSource) GetApiData(ctx context.Context, req *source.DataSourceApiGetReq) (res *source.DataSourceApiGetRes, err error) { - res = new(source.DataSourceApiGetRes) - data, err := service.DataSource().GetApiData(ctx, req.SourceId) - if err != nil { - return - } - if len(data) > 0 { - res.Data = data[0] - } - return -} - -// 批量删除数据源 -func (c *cDataSource) Del(ctx context.Context, req *source.DataSourceDelReq) (res *source.DataSourceDelRes, err error) { - err = service.DataSource().Del(ctx, req.Ids) - return -} - -// 搜索数据源 -func (c *cDataSource) Search(ctx context.Context, req *source.DataSourceSearchReq) (res *source.DataSourceSearchRes, err error) { - out, err := service.DataSource().Search(ctx, req.DataSourceSearchInput) - res = &source.DataSourceSearchRes{ - DataSourceSearchOutput: out, - } - return -} - -// 数据源列表 -func (c *cDataSource) List(ctx context.Context, req *source.DataSourceListReq) (res *source.DataSourceListRes, err error) { - list, err := service.DataSource().List(ctx) - res = &source.DataSourceListRes{ - List: list, - } - return -} - -// 详情 -func (c *cDataSource) Detail(ctx context.Context, req *source.DataSourceReq) (res *source.DataSourceRes, err error) { - res = new(source.DataSourceRes) - res.Data, err = service.DataSource().Detail(ctx, req.SourceId) - return -} - -// 发布 -func (c *cDataSource) Deploy(ctx context.Context, req *source.DataSourceDeployReq) (res *source.DataSourceDeployRes, err error) { - err = service.DataSource().Deploy(ctx, req.SourceId) - return -} - -// 停用 -func (c *cDataSource) Undeploy(ctx context.Context, req *source.DataSourceUndeployReq) (res *source.DataSourceUndeployRes, err error) { - err = service.DataSource().Undeploy(ctx, req.SourceId) - return -} - -// 获取源数据记录 -func (c *cDataSource) GetData(ctx context.Context, req *source.DataSourceDataReq) (res *source.DataSourceDataRes, err error) { - out, err := service.DataSource().GetData(ctx, req.DataSourceDataInput) - res = &source.DataSourceDataRes{ - DataSourceDataOutput: out, - } - return -} - -// 复制数据源 -func (c *cDataSource) Copy(ctx context.Context, req *source.DataSourceCopyReq) (res *source.DataSourceCopyRes, err error) { - err = service.DataSource().CopeSource(ctx, req.SourceId) - return -} diff --git a/internal/controller/source/source_db.go b/internal/controller/source/source_db.go deleted file mode 100644 index 0e1913f..0000000 --- a/internal/controller/source/source_db.go +++ /dev/null @@ -1,33 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -// 添加 数据库 数据源 -func (c *cDataSource) AddDb(ctx context.Context, req *source.DataSourceDbAddReq) (res *source.DataSourceDbAddRes, err error) { - _, err = service.DataSource().AddDb(ctx, req.DataSourceDbAddInput) - return -} - -// 编辑 数据库 数据源 -func (c *cDataSource) EditDb(ctx context.Context, req *source.DataSourceDbEditReq) (res *source.DataSourceDbEditRes, err error) { - err = service.DataSource().EditDb(ctx, req.DataSourceDbEditInput) - return -} - -// 获取 数据库 数据 -func (c *cDataSource) GetDbData(ctx context.Context, req *source.DataSourceDbGetReq) (res *source.DataSourceDbGetRes, err error) { - res = new(source.DataSourceDbGetRes) - res.Data, err = service.DataSource().GetDbData(ctx, req.SourceId) - return -} - -// 获取 数据表 字段 -func (c *cDataSource) GetDbFields(ctx context.Context, req *source.DataSourceDbFieldsReq) (res *source.DataSourceDbFieldsRes, err error) { - res = new(source.DataSourceDbFieldsRes) - res.Data, err = service.DataSource().GetDbFields(ctx, req.SourceId) - return -} diff --git a/internal/controller/source/source_device.go b/internal/controller/source/source_device.go deleted file mode 100644 index c600437..0000000 --- a/internal/controller/source/source_device.go +++ /dev/null @@ -1,26 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -// 添加 设备 数据源 -func (c *cDataSource) AddDevice(ctx context.Context, req *source.DataSourceDeviceAddReq) (res *source.DataSourceDeviceAddRes, err error) { - _, err = service.DataSource().AddDevice(ctx, req.DataSourceDeviceAddInput) - return -} - -// 编辑 设备 数据源 -func (c *cDataSource) EditDevice(ctx context.Context, req *source.DataSourceDeviceEditReq) (res *source.DataSourceDeviceEditRes, err error) { - err = service.DataSource().EditDevice(ctx, req.DataSourceDeviceEditInput) - return -} - -// 获取 设备 数据 -func (c *cDataSource) GetDeviceData(ctx context.Context, req *source.DataSourceDeviceGetReq) (res *source.DataSourceDeviceGetRes, err error) { - res = new(source.DataSourceDeviceGetRes) - res.Data, err = service.DataSource().GetDeviceData(ctx, req.SourceId) - return -} diff --git a/internal/controller/source/template.go b/internal/controller/source/template.go deleted file mode 100644 index afdb0a2..0000000 --- a/internal/controller/source/template.go +++ /dev/null @@ -1,99 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var DataTemplate = cDataTemplate{} - -type cDataTemplate struct{} - -// 添加数据模型 -func (c *cDataTemplate) Add(ctx context.Context, req *source.DataTemplateAddReq) (res *source.DataTemplateAddRes, err error) { - _, err = service.DataTemplate().Add(ctx, req.DataTemplateAddInput) - return -} - -// 编辑数据模型 -func (c *cDataTemplate) Edit(ctx context.Context, req *source.DataTemplateEditReq) (res *source.DataTemplateEditRes, err error) { - err = service.DataTemplate().Edit(ctx, req.DataTemplateEditInput) - return -} - -// 批量删除数据模型 -func (c *cDataTemplate) Del(ctx context.Context, req *source.DataTemplateDelReq) (res *source.DataTemplateDelRes, err error) { - err = service.DataTemplate().Del(ctx, req.Ids) - return -} - -// 搜索数据模型 -func (c *cDataTemplate) Search(ctx context.Context, req *source.DataTemplateSearchReq) (res *source.DataTemplateSearchRes, err error) { - out, err := service.DataTemplate().Search(ctx, req.DataTemplateSearchInput) - res = &source.DataTemplateSearchRes{ - DataTemplateSearchOutput: out, - } - return -} - -// 已发布数据模型列表 -func (c *cDataTemplate) List(ctx context.Context, req *source.DataTemplateListReq) (res *source.DataTemplateListRes, err error) { - list, err := service.DataTemplate().List(ctx) - res = &source.DataTemplateListRes{ - List: list, - } - return -} - -// 详情 -func (c *cDataTemplate) Detail(ctx context.Context, req *source.DataTemplateReq) (res *source.DataTemplateRes, err error) { - res = new(source.DataTemplateRes) - res.Data, err = service.DataTemplate().Detail(ctx, req.Id) - return -} - -// 发布 -func (c *cDataTemplate) Deploy(ctx context.Context, req *source.DataTemplateDeployReq) (res *source.DataTemplateDeployRes, err error) { - err = service.DataTemplate().Deploy(ctx, req.Id) - return -} - -// 停用 -func (c *cDataTemplate) Undeploy(ctx context.Context, req *source.DataTemplateUndeployReq) (res *source.DataTemplateUndeployRes, err error) { - err = service.DataTemplate().Undeploy(ctx, req.Id) - return -} - -// 获取模型数据 -func (c *cDataTemplate) GetData(ctx context.Context, req *source.DataTemplateDataReq) (res *source.DataTemplateDataRes, err error) { - out, err := service.DataTemplate().GetData(ctx, req.DataTemplateDataInput) - res = &source.DataTemplateDataRes{ - DataTemplateDataOutput: out, - } - return -} - -// 复制模型 -func (c *cDataTemplate) Copy(ctx context.Context, req *source.DataTemplateCopyReq) (res *source.DataTemplateCopyRes, err error) { - err = service.DataTemplate().CopeTemplate(ctx, req.Id) - return -} - -// 检测数据模型是否需要设置关联 -func (c *cDataTemplate) CheckRelation(ctx context.Context, req *source.DataTemplateCheckRelationReq) (res source.DataTemplateCheckRelationRes, err error) { - res.Yes, err = service.DataTemplate().CheckRelation(ctx, req.Id) - return -} - -// 设置主源、关联字段 -func (c *cDataTemplate) SetRelation(ctx context.Context, req *source.DataTemplateRelationReq) (res *source.DataTemplateRelationRes, err error) { - err = service.DataTemplate().SetRelation(ctx, req.TemplateDataRelationInput) - return -} - -// 数据源列表 -func (c *cDataTemplate) SourceList(ctx context.Context, req *source.TemplateSourceListReq) (res source.TemplateSourceListRes, err error) { - res.List, err = service.DataTemplate().SourceList(ctx, req.Id) - return -} diff --git a/internal/controller/source/template_node.go b/internal/controller/source/template_node.go deleted file mode 100644 index 404cfd2..0000000 --- a/internal/controller/source/template_node.go +++ /dev/null @@ -1,38 +0,0 @@ -package source - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/source" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var DataTemplateNode = cDataTemplateNode{} - -type cDataTemplateNode struct{} - -// 添加数据模型节点 -func (c *cDataTemplateNode) Add(ctx context.Context, req *source.DataTemplateNodeAddReq) (res *source.DataTemplateNodeAddRes, err error) { - err = service.DataTemplateNode().Add(ctx, req.DataTemplateNodeAddInput) - return -} - -// 编辑数据模型节点 -func (c *cDataTemplateNode) Edit(ctx context.Context, req *source.DataTemplateNodeEditReq) (res *source.DataTemplateNodeEditRes, err error) { - err = service.DataTemplateNode().Edit(ctx, req.DataTemplateNodeEditInput) - return -} - -// 删除数据模型节点 -func (c *cDataTemplateNode) Del(ctx context.Context, req *source.DataTemplateNodeDelReq) (res *source.DataTemplateNodeDelRes, err error) { - err = service.DataTemplateNode().Del(ctx, req.Id) - return -} - -// 数据模型节点列表 -func (c *cDataTemplateNode) List(ctx context.Context, req *source.DataTemplateNodeListReq) (res *source.DataTemplateNodeListRes, err error) { - list, err := service.DataTemplateNode().List(ctx, req.Tid) - res = &source.DataTemplateNodeListRes{ - List: list, - } - return -} diff --git a/internal/controller/system/captcha.go b/internal/controller/system/captcha.go index ec34264..7953167 100644 --- a/internal/controller/system/captcha.go +++ b/internal/controller/system/captcha.go @@ -2,9 +2,9 @@ package system import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/service" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" + systemV1 "sagooiot/api/v1/system" ) // 图形验证码 diff --git a/internal/controller/system/login.go b/internal/controller/system/login.go index 180b133..939be1e 100644 --- a/internal/controller/system/login.go +++ b/internal/controller/system/login.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) // Login 登录管理 @@ -15,7 +15,8 @@ type cLogin struct{} // Login 登录 func (a *cLogin) Login(ctx context.Context, req *system.LoginDoReq) (res *system.LoginDoRes, err error) { - out, token, err := service.Login().Login(ctx, req.VerifyKey, req.Captcha, req.UserName, req.Password) + + out, token, isChangePassword, err := service.Login().Login(ctx, req.VerifyKey, req.Captcha, req.UserName, req.Password) if err != nil { return } @@ -26,8 +27,9 @@ func (a *cLogin) Login(ctx context.Context, req *system.LoginDoReq) (res *system } } res = &system.LoginDoRes{ - UserInfo: loginUserRes, - Token: token, + UserInfo: loginUserRes, + Token: token, + IsChangePwd: isChangePassword, } return } @@ -37,3 +39,9 @@ func (a *cLogin) LoginOut(ctx context.Context, req *system.LoginOutReq) (res *sy err = service.Login().LoginOut(ctx) return } + +// EditPassword 修改密码 +func (a *cLogin) EditPassword(ctx context.Context, req *system.EditPasswordReq) (res *system.EditPasswordRes, err error) { + err = service.SysUser().EditPassword(ctx, req.UserName, req.OldUserPassword, req.UserPassword) + return +} diff --git a/internal/controller/system/sys_api.go b/internal/controller/system/sys_api.go index d51c60c..8c472b6 100644 --- a/internal/controller/system/sys_api.go +++ b/internal/controller/system/sys_api.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysApi = cApi{} @@ -14,13 +14,13 @@ type cApi struct{} // GetApiAll 获取所有接口 func (a *cApi) GetApiAll(ctx context.Context, req *system.GetApiAllReq) (res *system.GetApiAllRes, err error) { - apiInfo, err := service.SysApi().GetApiAll(ctx) + out, err := service.SysApi().GetApiAll(ctx, req.Method) if err != nil { return } var apiInfoRes []*model.SysApiAllRes - if apiInfo != nil { - if err = gconv.Scan(apiInfo, &apiInfoRes); err != nil { + if out != nil { + if err = gconv.Scan(out, &apiInfoRes); err != nil { return } } @@ -97,3 +97,17 @@ func (a *cApi) EditApiStatus(ctx context.Context, req *system.EditApiStatusReq) err = service.SysApi().EditStatus(ctx, req.Id, req.Status) return } + +// ImportApiFile 导入API文件 +func (a *cApi) ImportApiFile(ctx context.Context, req *system.ImportApiFileReq) (res *system.ImportApiFileRes, err error) { + err = service.SysApi().ImportApiFile(ctx) + return +} + +// BindApiMenus 批量绑定菜单 +func (a *cApi) BindApiMenus(ctx context.Context, req *system.BindApiMenusReq) (res *system.BindApiMenusRes, err error) { + for _, bindMenu := range req.BindMenus { + err = service.SysApi().AddMenuApi(ctx, "api", []int{bindMenu.Id}, bindMenu.MenuIds) + } + return +} diff --git a/internal/controller/system/sys_authorize.go b/internal/controller/system/sys_authorize.go index 332e6f5..9e0bdd4 100644 --- a/internal/controller/system/sys_authorize.go +++ b/internal/controller/system/sys_authorize.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysAuthorize = cSysAuthorize{} diff --git a/internal/controller/system/sys_certificate.go b/internal/controller/system/sys_certificate.go new file mode 100644 index 0000000..0d25588 --- /dev/null +++ b/internal/controller/system/sys_certificate.go @@ -0,0 +1,93 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +var SysCertificate = cSysCertificate{} + +type cSysCertificate struct{} + +// GetList 获取列表 +func (u *cSysCertificate) GetList(ctx context.Context, req *system.GetCertificateListReq) (res *system.GetCertificateListRes, err error) { + var input *model.SysCertificateListInput + if err = gconv.Scan(req, &input); err != nil { + return + } + total, currentPage, out, err := service.SysCertificate().GetList(ctx, input) + res = new(system.GetCertificateListRes) + res.PaginationRes.Total = total + res.PaginationRes.CurrentPage = currentPage + if out != nil && len(out) > 0 { + if err = gconv.Scan(out, &res.Info); err != nil { + return + } + } + return +} + +// GetCertificateById 获取指定ID数据 +func (u *cSysCertificate) GetCertificateById(ctx context.Context, req *system.GetCertificateByIdReq) (res *system.GetCertificateByIdRes, err error) { + out, err := service.SysCertificate().GetInfoById(ctx, req.Id) + var info *model.SysCertificateOut + if out != nil { + if err = gconv.Scan(out, &info); err != nil { + return + } + } + res = &system.GetCertificateByIdRes{ + Info: info, + } + return +} + +// AddCertificate 添加数据 +func (u *cSysCertificate) AddCertificate(ctx context.Context, req *system.AddCertificateReq) (res *system.AddCertificateRes, err error) { + var input *model.AddSysCertificateListInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysCertificate().Add(ctx, input) + return +} + +// EditCertificate 修改数据 +func (u *cSysCertificate) EditCertificate(ctx context.Context, req *system.EditCertificateReq) (res *system.EditCertificateRes, err error) { + var input *model.EditSysCertificateListInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysCertificate().Edit(ctx, input) + return +} + +// DeleteCertificate 删除数据 +func (u *cSysCertificate) DeleteCertificate(ctx context.Context, req *system.DeleteCertificateReq) (res *system.DeleteCertificateRes, err error) { + err = service.SysCertificate().Delete(ctx, req.Id) + return +} + +// EditCertificateStatus 修改数据 +func (u *cSysCertificate) EditCertificateStatus(ctx context.Context, req *system.EditCertificateStatusReq) (res *system.EditCertificateStatusRes, err error) { + err = service.SysCertificate().EditStatus(ctx, req.Id, req.Status) + return +} + +// GetCertificateAll 获取所有证书 +func (u *cSysCertificate) GetCertificateAll(ctx context.Context, req *system.GetCertificateAllReq) (res *system.GetCertificateAllRes, err error) { + out, err := service.SysCertificate().GetAll(ctx) + var info []*model.SysCertificateOut + if out != nil { + if err = gconv.Scan(out, &info); err != nil { + return + } + } + res = &system.GetCertificateAllRes{ + Info: info, + } + return +} diff --git a/internal/controller/system/sys_dept.go b/internal/controller/system/sys_dept.go index b608f1a..3d162a7 100644 --- a/internal/controller/system/sys_dept.go +++ b/internal/controller/system/sys_dept.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + systemV1 "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysDept = cDept{} diff --git a/internal/controller/system/sys_job.go b/internal/controller/system/sys_job.go index 3a0a872..83bb0e5 100644 --- a/internal/controller/system/sys_job.go +++ b/internal/controller/system/sys_job.go @@ -4,9 +4,9 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysJob = cSysJob{} @@ -39,6 +39,15 @@ func (a *cSysJob) List(ctx context.Context, req *system.GetJobListReq) (res *sys return } +// FunList 获取任务可用方法名列表 +func (a *cSysJob) FunList(ctx context.Context, req *system.GetJobFunListReq) (res *system.GetJobFunListRes, err error) { + resData, err := service.SysJob().GetJobFuns(ctx) + res = &system.GetJobFunListRes{ + Data: resData, + } + return +} + func (a *cSysJob) Add(ctx context.Context, req *system.AddJobReq) (res *system.AddJobRes, err error) { //获取当前登录用户ID diff --git a/internal/controller/system/sys_login_log.go b/internal/controller/system/sys_login_log.go index bab1c1a..7994363 100644 --- a/internal/controller/system/sys_login_log.go +++ b/internal/controller/system/sys_login_log.go @@ -2,15 +2,11 @@ package system import ( "context" - "github.com/gogf/gf/v2/frame/g" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility" - "github.com/sagoo-cloud/sagooiot/utility/response" - "github.com/gogf/gf/v2/util/gconv" + systemV1 "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" ) var SysLoginLog = cSysLoginLog{} @@ -46,19 +42,7 @@ func (a *cSysLoginLog) Export(ctx context.Context, req *systemV1.SysLoginLogDoEx if err != nil { return } - _, _, outList, err := service.SysLoginLog().GetList(ctx, reqData) - if err != nil { - return - } - - //处理数据并导出 - var resData []interface{} - for _, d := range outList { - resData = append(resData, d) - } - data := utility.ToExcel(resData) - var request = g.RequestFromCtx(ctx) - response.ToXls(request, data, "SysLoginLog") + err = service.SysLoginLog().Export(ctx, reqData) return } diff --git a/internal/controller/system/sys_menu.go b/internal/controller/system/sys_menu.go index 6da9bd3..f11eff9 100644 --- a/internal/controller/system/sys_menu.go +++ b/internal/controller/system/sys_menu.go @@ -3,20 +3,20 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysMenu = cMenu{} type cMenu struct{} -func (a *cMenu) MenuTree(ctx context.Context, req *system.MenuDoReq) (res *system.MenuDoRes, err error) { +func (s *cMenu) MenuTree(ctx context.Context, req *system.MenuDoReq) (res *system.MenuDoRes, err error) { //获取所有的菜单 menuInfo, err := service.SysMenu().GetTree(ctx, req.Title, req.Status) if err != nil { - return nil, err + return } var treeData []*model.SysMenuRes if menuInfo != nil { @@ -31,7 +31,7 @@ func (a *cMenu) MenuTree(ctx context.Context, req *system.MenuDoReq) (res *syste } // AddMenu 添加菜单 -func (a *cMenu) AddMenu(ctx context.Context, req *system.AddMenuReq) (res *system.AddMenuRes, err error) { +func (s *cMenu) AddMenu(ctx context.Context, req *system.AddMenuReq) (res *system.AddMenuRes, err error) { var input *model.AddMenuInput if err = gconv.Scan(req, &input); err != nil { return @@ -41,7 +41,7 @@ func (a *cMenu) AddMenu(ctx context.Context, req *system.AddMenuReq) (res *syste } // DetailMenu 获取菜单详情 -func (a *cMenu) DetailMenu(ctx context.Context, req *system.DetailMenuReq) (res *system.DetailMenuRes, err error) { +func (s *cMenu) DetailMenu(ctx context.Context, req *system.DetailMenuReq) (res *system.DetailMenuRes, err error) { data, err := service.SysMenu().Detail(ctx, req.Id) if err != nil { return nil, err @@ -59,7 +59,7 @@ func (a *cMenu) DetailMenu(ctx context.Context, req *system.DetailMenuReq) (res } // EditMenu 编辑菜单 -func (a *cMenu) EditMenu(ctx context.Context, req *system.EditMenuReq) (res *system.EditMenuRes, err error) { +func (s *cMenu) EditMenu(ctx context.Context, req *system.EditMenuReq) (res *system.EditMenuRes, err error) { var input *model.EditMenuInput if err = gconv.Scan(req, &input); err != nil { return @@ -69,203 +69,7 @@ func (a *cMenu) EditMenu(ctx context.Context, req *system.EditMenuReq) (res *sys } // DelMenu 根据ID删除菜单 -func (a *cMenu) DelMenu(ctx context.Context, req *system.DelMenuReq) (res *system.DelMenuRes, err error) { +func (s *cMenu) DelMenu(ctx context.Context, req *system.DelMenuReq) (res *system.DelMenuRes, err error) { err = service.SysMenu().Del(ctx, req.Id) return } - -// MenuButtonTree 获取菜单按钮树结构列表 -func (a *cMenu) MenuButtonTree(ctx context.Context, req *system.MenuButtonDoReq) (res *system.MenuButtonDoRes, err error) { - //获取所有的菜单 - menuButtonInfo, err := service.SysMenuButton().GetList(ctx, req.Status, req.Name, req.MenuId) - if err != nil { - return nil, err - } - var parentNodeRes []*model.UserMenuButtonRes - if menuButtonInfo != nil { - //获取所有的根节点 - for _, v := range menuButtonInfo { - var parentNode *model.UserMenuButtonRes - if v.ParentId == -1 { - if err = gconv.Scan(v, &parentNode); err != nil { - return - } - parentNodeRes = append(parentNodeRes, parentNode) - } - } - } - treeData := buttonTree(parentNodeRes, menuButtonInfo) - res = &system.MenuButtonDoRes{ - Data: treeData, - } - return -} - -// buttonTree MenuButtonTree 生成菜单按钮树结构 -func buttonTree(parentNodeRes []*model.UserMenuButtonRes, data []model.UserMenuButtonRes) (dataTree []*model.UserMenuButtonRes) { - //循环所有一级菜单 - for k, v := range parentNodeRes { - //查询所有该菜单下的所有子菜单 - for _, j := range data { - var node *model.UserMenuButtonRes - if j.ParentId == v.Id { - if err := gconv.Scan(j, &node); err != nil { - return - } - parentNodeRes[k].Children = append(parentNodeRes[k].Children, node) - } - } - buttonTree(v.Children, data) - } - return parentNodeRes -} - -// AddMenuButton AddMenu 添加菜单按钮 -func (a *cMenu) AddMenuButton(ctx context.Context, req *system.AddMenuButtonReq) (res *system.AddMenuButtonRes, err error) { - var input *model.AddMenuButtonInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.SysMenuButton().Add(ctx, input) - return -} - -// DetailMenuButton DetailMenu 获取菜单按钮详情 -func (a *cMenu) DetailMenuButton(ctx context.Context, req *system.DetailMenuButtonReq) (res *system.DetailMenuButtonRes, err error) { - data, err := service.SysMenuButton().Detail(ctx, req.Id) - if err != nil { - return nil, err - } - if data != nil { - var detailRes *model.DetailMenuButtonRes - if err = gconv.Scan(data, &detailRes); err != nil { - return nil, err - } - res = &system.DetailMenuButtonRes{ - Data: detailRes, - } - } - return -} - -// EditMenuButton 编辑菜单按钮 -func (a *cMenu) EditMenuButton(ctx context.Context, req *system.EditMenuButtonReq) (res *system.EditMenuButtonRes, err error) { - var input *model.EditMenuButtonInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.SysMenuButton().Edit(ctx, input) - return -} - -// DelMenuButton 根据ID删除菜单按钮 -func (a *cMenu) DelMenuButton(ctx context.Context, req *system.DelMenuButtonReq) (res *system.DelMenuButtonRes, err error) { - err = service.SysMenuButton().Del(ctx, req.Id) - return -} - -// EditMenuButtonStatus 编辑菜单按钮状态 -func (a *cMenu) EditMenuButtonStatus(ctx context.Context, req *system.EditMenuButtonStatusReq) (res *system.EditMenuButtonStatusRes, err error) { - err = service.SysMenuButton().EditStatus(ctx, req.Id, req.MenuId, req.Status) - return -} - -// MenuColumnTree 获取菜单列表树结构列表 -func (a *cMenu) MenuColumnTree(ctx context.Context, req *system.MenuColumnDoReq) (res *system.MenuColumnDoRes, err error) { - var input *model.MenuColumnDoInput - if err = gconv.Scan(req, &input); err != nil { - return - } - //获取所有的菜单 - menuColumnInfo, err := service.SysMenuColumn().GetList(ctx, input) - if err != nil { - return nil, err - } - var parentNodeRes []*model.UserMenuColumnRes - if menuColumnInfo != nil { - //获取所有的根节点 - for _, v := range menuColumnInfo { - var parentNode *model.UserMenuColumnRes - if v.ParentId == -1 { - if err = gconv.Scan(v, &parentNode); err != nil { - return - } - parentNodeRes = append(parentNodeRes, parentNode) - } - } - } - treeData := ColumnTree(parentNodeRes, menuColumnInfo) - res = &system.MenuColumnDoRes{ - Data: treeData, - } - return -} - -// ColumnTree MenuColumnTree 生成菜单列表树结构 -func ColumnTree(parentNodeRes []*model.UserMenuColumnRes, data []model.UserMenuColumnRes) (dataTree []*model.UserMenuColumnRes) { - //循环所有一级菜单 - for k, v := range parentNodeRes { - //查询所有该菜单下的所有子菜单 - for _, j := range data { - var node *model.UserMenuColumnRes - if j.ParentId == v.Id { - if err := gconv.Scan(j, &node); err != nil { - return - } - parentNodeRes[k].Children = append(parentNodeRes[k].Children, node) - } - } - ColumnTree(v.Children, data) - } - return parentNodeRes -} - -// AddMenuColumn AddMenu 添加菜单列表 -func (a *cMenu) AddMenuColumn(ctx context.Context, req *system.AddMenuColumnReq) (res *system.AddMenuColumnRes, err error) { - var input *model.AddMenuColumnInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.SysMenuColumn().Add(ctx, input) - return -} - -// DetailMenuColumn DetailMenu 获取菜单列表详情 -func (a *cMenu) DetailMenuColumn(ctx context.Context, req *system.DetailMenuColumnReq) (res *system.DetailMenuColumnRes, err error) { - data, err := service.SysMenuColumn().Detail(ctx, req.Id) - if err != nil { - return nil, err - } - if data != nil { - var detailRes *model.DetailMenuColumnRes - if err = gconv.Scan(data, &detailRes); err != nil { - return nil, err - } - res = &system.DetailMenuColumnRes{ - Data: detailRes, - } - } - return -} - -// EditMenuColumn EditMenu 编辑菜单列表 -func (a *cMenu) EditMenuColumn(ctx context.Context, req *system.EditMenuColumnReq) (res *system.EditMenuColumnRes, err error) { - var input *model.EditMenuColumnInput - if err = gconv.Scan(req, &input); err != nil { - return - } - err = service.SysMenuColumn().Edit(ctx, input) - return -} - -// DelMenuColumn DelMenu 根据ID删除菜单列表 -func (a *cMenu) DelMenuColumn(ctx context.Context, req *system.DelMenuColumnReq) (res *system.DelMenuColumnRes, err error) { - err = service.SysMenuColumn().Del(ctx, req.Id) - return -} - -// EditMenuColumnStatus 编辑菜单列表状态 -func (a *cMenu) EditMenuColumnStatus(ctx context.Context, req *system.EditMenuColumnStatusReq) (res *system.EditMenuColumnStatusRes, err error) { - err = service.SysMenuColumn().EditStatus(ctx, req.Id, req.MenuId, req.Status) - return -} diff --git a/internal/controller/system/sys_menu_api.go b/internal/controller/system/sys_menu_api.go new file mode 100644 index 0000000..8e999cf --- /dev/null +++ b/internal/controller/system/sys_menu_api.go @@ -0,0 +1,40 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +var SysMenuApi = cMenuApi{} + +type cMenuApi struct{} + +// MenuApiTree 获取菜单API树结构列表 +func (c *cMenuApi) MenuApiTree(ctx context.Context, req *system.MenuApiDoReq) (res *system.MenuApiDoRes, err error) { + out, err := service.SysMenuApi().MenuApiList(ctx, req.MenuId) + if err != nil { + return + } + var data []*model.SysApiAllRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &system.MenuApiDoRes{ + Data: data, + } + return +} + +// AddMenuApi 绑定菜单和API关联关系 +func (c *cMenuApi) AddMenuApi(ctx context.Context, req *system.AddMenuApiReq) (res *system.AddMenuApiRes, err error) { + err = service.SysApi().AddMenuApi(ctx, "menu", req.ApiIds, []int{req.MenuId}) + if err != nil { + return + } + return +} diff --git a/internal/controller/system/sys_menu_button.go b/internal/controller/system/sys_menu_button.go new file mode 100644 index 0000000..1500181 --- /dev/null +++ b/internal/controller/system/sys_menu_button.go @@ -0,0 +1,81 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +var SysMenuButton = cMenuButton{} + +type cMenuButton struct{} + +// MenuButtonTree 获取菜单按钮树结构列表 +func (c *cMenuButton) MenuButtonTree(ctx context.Context, req *system.MenuButtonDoReq) (res *system.MenuButtonDoRes, err error) { + out, err := service.SysMenuButton().GetList(ctx, req.Status, req.Name, req.MenuId) + if err != nil { + return + } + var data []*model.UserMenuButtonRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &system.MenuButtonDoRes{ + Data: data, + } + return +} + +// AddMenuButton AddMenu 添加菜单按钮 +func (c *cMenuButton) AddMenuButton(ctx context.Context, req *system.AddMenuButtonReq) (res *system.AddMenuButtonRes, err error) { + var input *model.AddMenuButtonInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysMenuButton().Add(ctx, input) + return +} + +// DetailMenuButton DetailMenu 获取菜单按钮详情 +func (c *cMenuButton) DetailMenuButton(ctx context.Context, req *system.DetailMenuButtonReq) (res *system.DetailMenuButtonRes, err error) { + data, err := service.SysMenuButton().Detail(ctx, req.Id) + if err != nil { + return nil, err + } + if data != nil { + var detailRes *model.DetailMenuButtonRes + if err = gconv.Scan(data, &detailRes); err != nil { + return nil, err + } + res = &system.DetailMenuButtonRes{ + Data: detailRes, + } + } + return +} + +// EditMenuButton 编辑菜单按钮 +func (c *cMenuButton) EditMenuButton(ctx context.Context, req *system.EditMenuButtonReq) (res *system.EditMenuButtonRes, err error) { + var input *model.EditMenuButtonInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysMenuButton().Edit(ctx, input) + return +} + +// DelMenuButton 根据ID删除菜单按钮 +func (c *cMenuButton) DelMenuButton(ctx context.Context, req *system.DelMenuButtonReq) (res *system.DelMenuButtonRes, err error) { + err = service.SysMenuButton().Del(ctx, req.Id) + return +} + +// EditMenuButtonStatus 编辑菜单按钮状态 +func (c *cMenuButton) EditMenuButtonStatus(ctx context.Context, req *system.EditMenuButtonStatusReq) (res *system.EditMenuButtonStatusRes, err error) { + err = service.SysMenuButton().EditStatus(ctx, req.Id, req.MenuId, req.Status) + return +} diff --git a/internal/controller/system/sys_menu_column.go b/internal/controller/system/sys_menu_column.go new file mode 100644 index 0000000..0ad6a91 --- /dev/null +++ b/internal/controller/system/sys_menu_column.go @@ -0,0 +1,85 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +var SysMenuColumn = cMenuColumn{} + +type cMenuColumn struct{} + +// MenuColumnTree 获取菜单列表树结构列表 +func (c *cMenuColumn) MenuColumnTree(ctx context.Context, req *system.MenuColumnDoReq) (res *system.MenuColumnDoRes, err error) { + var input *model.MenuColumnDoInput + if err = gconv.Scan(req, &input); err != nil { + return + } + out, err := service.SysMenuColumn().GetList(ctx, input) + if err != nil { + return + } + var data []*model.UserMenuColumnRes + if out != nil { + if err = gconv.Scan(out, &data); err != nil { + return + } + } + res = &system.MenuColumnDoRes{ + Data: data, + } + return +} + +// AddMenuColumn AddMenu 添加菜单列表 +func (c *cMenuColumn) AddMenuColumn(ctx context.Context, req *system.AddMenuColumnReq) (res *system.AddMenuColumnRes, err error) { + var input *model.AddMenuColumnInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysMenuColumn().Add(ctx, input) + return +} + +// DetailMenuColumn DetailMenu 获取菜单列表详情 +func (c *cMenuColumn) DetailMenuColumn(ctx context.Context, req *system.DetailMenuColumnReq) (res *system.DetailMenuColumnRes, err error) { + data, err := service.SysMenuColumn().Detail(ctx, req.Id) + if err != nil { + return nil, err + } + if data != nil { + var detailRes *model.DetailMenuColumnRes + if err = gconv.Scan(data, &detailRes); err != nil { + return nil, err + } + res = &system.DetailMenuColumnRes{ + Data: detailRes, + } + } + return +} + +// EditMenuColumn EditMenu 编辑菜单列表 +func (c *cMenuColumn) EditMenuColumn(ctx context.Context, req *system.EditMenuColumnReq) (res *system.EditMenuColumnRes, err error) { + var input *model.EditMenuColumnInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysMenuColumn().Edit(ctx, input) + return +} + +// DelMenuColumn DelMenu 根据ID删除菜单列表 +func (c *cMenuColumn) DelMenuColumn(ctx context.Context, req *system.DelMenuColumnReq) (res *system.DelMenuColumnRes, err error) { + err = service.SysMenuColumn().Del(ctx, req.Id) + return +} + +// EditMenuColumnStatus 编辑菜单列表状态 +func (c *cMenuColumn) EditMenuColumnStatus(ctx context.Context, req *system.EditMenuColumnStatusReq) (res *system.EditMenuColumnStatusRes, err error) { + err = service.SysMenuColumn().EditStatus(ctx, req.Id, req.MenuId, req.Status) + return +} diff --git a/internal/controller/system/sys_message.go b/internal/controller/system/sys_message.go new file mode 100644 index 0000000..483c7c7 --- /dev/null +++ b/internal/controller/system/sys_message.go @@ -0,0 +1,103 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +var SysMessage = cSysMessage{} + +type cSysMessage struct{} + +// GetMessageList 获取消息列表 +func (a *cSysMessage) GetMessageList(ctx context.Context, req *system.GetMessageListReq) (res *system.GetMessageListRes, err error) { + var input *model.MessageListDoInput + if err = gconv.Scan(req, &input); err != nil { + return + } + + total, out, err := service.SysMessage().GetList(ctx, input) + if err != nil { + return + } + res = new(system.GetMessageListRes) + res.Total = total + res.CurrentPage = req.PageNum + if out != nil { + if err = gconv.Scan(out, &res.Info); err != nil { + return + } + } + return +} + +/*// AddMessage 添加消息 +func (a *cSysMessage) AddMessage(ctx context.Context, req *system.AddMessageReq) (res *system.AddMessageRes, err error) { + var message *model.AddMessageReq + if err = gconv.Scan(req.AddMessageReq, &message); err != nil { + return + } + err = service.SysMessage().Add(ctx, message) + return +}*/ + +// GetUnReadMessageAll 获取所有未读消息列表 +func (a *cSysMessage) GetUnReadMessageAll(ctx context.Context, req *system.GetUnReadMessageAllReq) (res *system.GetUnReadMessageAllRes, err error) { + var input *model.MessageListDoInput + if err = gconv.Scan(req, &input); err != nil { + return + } + + total, out, err := service.SysMessage().GetUnReadMessageAll(ctx, input) + if err != nil { + return + } + res = new(system.GetUnReadMessageAllRes) + res.Total = total + res.CurrentPage = req.PageNum + if out != nil { + if err = gconv.Scan(out, &res.Info); err != nil { + return + } + } + return +} + +// GetUnReadMessageCount 获取所有未读消息数量 +func (a *cSysMessage) GetUnReadMessageCount(ctx context.Context, req *system.GetUnReadMessageCountReq) (res *system.GetUnReadMessageCountRes, err error) { + out, err := service.SysMessage().GetUnReadMessageCount(ctx) + if err != nil { + return + } + res = &system.GetUnReadMessageCountRes{ + Count: out, + } + return +} + +// DelMessage 删除消息 +func (a *cSysMessage) DelMessage(ctx context.Context, req *system.DelMessageReq) (res *system.DelMessageRes, err error) { + err = service.SysMessage().DelMessage(ctx, req.Ids) + return +} + +// ClearMessage 一键清空消息 +func (a *cSysMessage) ClearMessage(ctx context.Context, req *system.ClearMessageReq) (res *system.ClearMessageRes, err error) { + err = service.SysMessage().ClearMessage(ctx) + return +} + +// ReadMessage 阅读消息 +func (a *cSysMessage) ReadMessage(ctx context.Context, req *system.ReadMessageReq) (res *system.ReadMessageRes, err error) { + err = service.SysMessage().ReadMessage(ctx, req.Id) + return +} + +// ReadMessageAll 全部阅读消息 +func (a *cSysMessage) ReadMessageAll(ctx context.Context, req *system.ReadMessageAllReq) (res *system.ReadMessageAllRes, err error) { + err = service.SysMessage().ReadMessageAll(ctx) + return +} diff --git a/internal/controller/system/sys_monitor.go b/internal/controller/system/sys_monitor.go deleted file mode 100644 index 29f7896..0000000 --- a/internal/controller/system/sys_monitor.go +++ /dev/null @@ -1,150 +0,0 @@ -package system - -import ( - "context" - "fmt" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/utility/utils" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/disk" - "github.com/shirou/gopsutil/v3/host" - "github.com/shirou/gopsutil/v3/load" - "github.com/shirou/gopsutil/v3/mem" - "os" - "runtime" - "strconv" - "time" -) - -var SysMonitor = cMonitor{ - startTime: gtime.Now(), -} - -type cMonitor struct { - startTime *gtime.Time -} - -func (c *cMonitor) List(ctx context.Context, req *system.MonitorSearchReq) (res *system.MonitorSearchRes, err error) { - cpuNum := runtime.NumCPU() //核心数 - var cpuUsed float64 = 0 //用户使用率 - var cpuAvg5 float64 = 0 //CPU负载5 - var cpuAvg15 float64 = 0 //当前空闲率 - - cpuInfo, err := cpu.Percent(time.Second, false) - if err == nil { - cpuUsed, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", cpuInfo[0]), 64) - } - - loadInfo, err := load.Avg() - if err == nil { - cpuAvg5, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", loadInfo.Load5), 64) - cpuAvg15, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", loadInfo.Load5), 64) - } - - var memTotal uint64 = 0 //总内存 - var memUsed uint64 = 0 //总内存 := 0 //已用内存 - var memFree uint64 = 0 //剩余内存 - var memUsage float64 = 0 //使用率 - - v, err := mem.VirtualMemory() - if err == nil { - memTotal = v.Total - memUsed = v.Used - memFree = memTotal - memUsed - memUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", v.UsedPercent), 64) - } - - var goTotal uint64 = 0 //go分配的总内存数 - var goUsed uint64 = 0 //go使用的内存数 - var goFree uint64 = 0 //go剩余的内存数 - var goUsage float64 = 0 //使用率 - - var gomem runtime.MemStats - runtime.ReadMemStats(&gomem) - goUsed = gomem.Sys - goUsage = gconv.Float64(fmt.Sprintf("%.2f", gconv.Float64(goUsed)/gconv.Float64(memTotal)*100)) - sysComputerIp := "" //服务器IP - - ip, err := utils.GetLocalIP() - if err == nil { - sysComputerIp = ip - } - - sysComputerName := "" //服务器名称 - sysOsName := "" //操作系统 - sysOsArch := "" //系统架构 - - sysInfo, err := host.Info() - - if err == nil { - sysComputerName = sysInfo.Hostname - sysOsName = sysInfo.OS - sysOsArch = sysInfo.KernelArch - } - - goName := "GoLang" //语言环境 - goVersion := runtime.Version() //版本 - gtime.Date() - goStartTime := c.startTime //启动时间 - - goRunTime := gtime.Now().Timestamp() - c.startTime.Timestamp() //运行时长(秒) - goHome := runtime.GOROOT() //安装路径 - goUserDir := "" //项目路径 - - curDir, err := os.Getwd() - - if err == nil { - goUserDir = curDir - } - - //服务器磁盘信息 - diskList := make([]disk.UsageStat, 0) - diskInfo, err := disk.Partitions(true) //所有分区 - if err == nil { - for _, p := range diskInfo { - diskDetail, err := disk.Usage(p.Mountpoint) - if err == nil { - diskDetail.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", diskDetail.UsedPercent), 64) - diskList = append(diskList, *diskDetail) - } - } - } - - d, _ := disk.Usage("/") - diskTotal := d.Total / 1024 / 1024 / 1024 //磁盘空间总数 - diskUsed := d.Used / 1024 / 1024 / 1024 //磁盘使用数 - diskUsedPercent := d.UsedPercent //磁盘使用率 - - res = new(system.MonitorSearchRes) - res = &system.MonitorSearchRes{ - "cpuNum": cpuNum, - "cpuUsed": cpuUsed, - "cpuAvg5": gconv.String(cpuAvg5), - "cpuAvg15": gconv.String(cpuAvg15), - "memTotal": memTotal, - "goTotal": goTotal, - "memUsed": memUsed, - "goUsed": goUsed, - "memFree": memFree, - "goFree": goFree, - "memUsage": memUsage, - "goUsage": goUsage, - "sysComputerName": sysComputerName, - "sysOsName": sysOsName, - "sysComputerIp": sysComputerIp, - "sysOsArch": sysOsArch, - "goName": goName, - "goVersion": goVersion, - "goStartTime": goStartTime, - "goRunTime": goRunTime, - "goHome": goHome, - "goUserDir": goUserDir, - "diskList": diskList, - "diskTotal": diskTotal, - "diskUsed": diskUsed, - "diskUsedPercent": diskUsedPercent, - } - return -} diff --git a/internal/controller/system/sys_notifications.go b/internal/controller/system/sys_notifications.go index 43e50dd..63fd21e 100644 --- a/internal/controller/system/sys_notifications.go +++ b/internal/controller/system/sys_notifications.go @@ -4,9 +4,9 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysNotifications = cSysNotifications{} diff --git a/internal/controller/system/sys_oper_log.go b/internal/controller/system/sys_oper_log.go index bd909b8..a96b6c5 100644 --- a/internal/controller/system/sys_oper_log.go +++ b/internal/controller/system/sys_oper_log.go @@ -2,10 +2,10 @@ package system import ( "context" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + systemV1 "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" "github.com/gogf/gf/v2/util/gconv" ) diff --git a/internal/controller/system/sys_organization.go b/internal/controller/system/sys_organization.go index 9024292..180942d 100644 --- a/internal/controller/system/sys_organization.go +++ b/internal/controller/system/sys_organization.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + systemV1 "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysOrganization = cOrganization{} diff --git a/internal/controller/system/sys_plugins.go b/internal/controller/system/sys_plugins.go index 547881e..8825627 100644 --- a/internal/controller/system/sys_plugins.go +++ b/internal/controller/system/sys_plugins.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysPlugins = cSystemSysPlugins{} @@ -28,18 +28,54 @@ func (u *cSystemSysPlugins) GetSysPluginsList(ctx context.Context, req *system.G // GetSysPluginsById 获取指定ID数据 func (u *cSystemSysPlugins) GetSysPluginsById(ctx context.Context, req *system.GetSysPluginsByIdReq) (res *system.GetSysPluginsByIdRes, err error) { - data, err := service.SysPlugins().GetSysPluginsById(ctx, req.Id) - res = new(system.GetSysPluginsByIdRes) - err = gconv.Scan(data, &res) + out, err := service.SysPlugins().GetSysPluginsById(ctx, req.Id) + if err != nil { + return + } + if out != nil { + res = new(system.GetSysPluginsByIdRes) + err = gconv.Scan(out, &res) + } + + return +} + +// AddSysPlugins 添加插件 +func (u *cSystemSysPlugins) AddSysPlugins(ctx context.Context, req *system.AddSysPluginsReq) (res *system.AddSysPluginsRes, err error) { + err = service.SysPlugins().AddSysPlugins(ctx, req.File) + if err != nil { + return + } + return } // EditSysPluginsStatus 修改插件的状态 -func (a *cMenu) EditSysPluginsStatus(ctx context.Context, req *system.EditSysPluginsStatusReq) (res *system.EditSysPluginsStatusRes, err error) { +func (u *cSystemSysPlugins) EditSysPluginsStatus(ctx context.Context, req *system.EditSysPluginsStatusReq) (res *system.EditSysPluginsStatusRes, err error) { err = service.SysPlugins().EditStatus(ctx, req.Id, req.Status) return } +// EditSysPlugins 添加插件 +func (u *cSystemSysPlugins) EditSysPlugins(ctx context.Context, req *system.EditSysPluginsReq) (res *system.EditSysPluginsRes, err error) { + var input *model.SysPluginsEditInput + if err = gconv.Scan(req, &input); err != nil { + return + } + err = service.SysPlugins().EditSysPlugins(ctx, input) + if err != nil { + return + } + + return +} + +// DelSysPluginsStatus 删除插件 +func (u *cSystemSysPlugins) DelSysPluginsStatus(ctx context.Context, req *system.DelSysPluginsStatusReq) (res *system.DelSysPluginsStatusRes, err error) { + err = service.SysPlugins().DeleteSysPlugins(ctx, req.Ids) + return +} + // GetSysPluginsTypesAll 获取插件通信方式类型 func (u *cSystemSysPlugins) GetSysPluginsTypesAll(ctx context.Context, req *system.GetSysPluginsTypesAllReq) (res *system.GetSysPluginsTypesAllRes, err error) { out, err := service.SysPlugins().GetSysPluginsTypesAll(ctx, req.Types) diff --git a/internal/controller/system/sys_plugins_config.go b/internal/controller/system/sys_plugins_config.go index aa69daa..6f6040f 100644 --- a/internal/controller/system/sys_plugins_config.go +++ b/internal/controller/system/sys_plugins_config.go @@ -4,31 +4,33 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) -var SysPluginsConfig = csystempluginsConfig{} +var SysPluginsConfig = cSystemPluginsConfig{} -type csystempluginsConfig struct{} +type cSystemPluginsConfig struct{} // GetPluginsConfigList 获取列表 -func (u *csystempluginsConfig) GetPluginsConfigList(ctx context.Context, req *system.GetPluginsConfigListReq) (res *system.GetPluginsConfigListRes, err error) { +func (u *cSystemPluginsConfig) GetPluginsConfigList(ctx context.Context, req *system.GetPluginsConfigListReq) (res *system.GetPluginsConfigListRes, err error) { var inputData = new(model.GetPluginsConfigListInput) if err = gconv.Scan(req, &inputData); err != nil { return } total, currentPage, dataList, err := service.SystemPluginsConfig().GetPluginsConfigList(ctx, inputData) res = new(system.GetPluginsConfigListRes) - err = gconv.Scan(dataList, &res.Data) + if err = gconv.Scan(dataList, &res.Data); err != nil { + return + } res.PaginationRes.Total = total res.PaginationRes.CurrentPage = currentPage return } // GetPluginsConfigById 获取指定ID数据 -func (u *csystempluginsConfig) GetPluginsConfigById(ctx context.Context, req *system.GetPluginsConfigByIdReq) (res *system.GetPluginsConfigByIdRes, err error) { +func (u *cSystemPluginsConfig) GetPluginsConfigById(ctx context.Context, req *system.GetPluginsConfigByIdReq) (res *system.GetPluginsConfigByIdRes, err error) { data, err := service.SystemPluginsConfig().GetPluginsConfigById(ctx, req.Id) res = new(system.GetPluginsConfigByIdRes) err = gconv.Scan(data, &res) @@ -36,15 +38,19 @@ func (u *csystempluginsConfig) GetPluginsConfigById(ctx context.Context, req *sy } // GetPluginsConfigByName 获取指定类型与名称的插件配置数据 -func (u *csystempluginsConfig) GetPluginsConfigByName(ctx context.Context, req *system.GetPluginsConfigByNameReq) (res *system.GetPluginsConfigByNameRes, err error) { +func (u *cSystemPluginsConfig) GetPluginsConfigByName(ctx context.Context, req *system.GetPluginsConfigByNameReq) (res *system.GetPluginsConfigByNameRes, err error) { data, err := service.SystemPluginsConfig().GetPluginsConfigByName(ctx, req.Type, req.Name) res = new(system.GetPluginsConfigByNameRes) - err = gconv.Scan(data, &res) + if data != nil { + if err = gconv.Scan(data, &res); err != nil { + return + } + } return } // AddPluginsConfig 添加数据 -func (u *csystempluginsConfig) AddPluginsConfig(ctx context.Context, req *system.AddPluginsConfigReq) (res *system.AddPluginsConfigRes, err error) { +func (u *cSystemPluginsConfig) AddPluginsConfig(ctx context.Context, req *system.AddPluginsConfigReq) (res *system.AddPluginsConfigRes, err error) { var data = model.PluginsConfigAddInput{} if err = gconv.Scan(req, &data); err != nil { return @@ -54,7 +60,7 @@ func (u *csystempluginsConfig) AddPluginsConfig(ctx context.Context, req *system } // EditPluginsConfig 修改数据 -func (u *csystempluginsConfig) EditPluginsConfig(ctx context.Context, req *system.EditPluginsConfigReq) (res *system.EditPluginsConfigRes, err error) { +func (u *cSystemPluginsConfig) EditPluginsConfig(ctx context.Context, req *system.EditPluginsConfigReq) (res *system.EditPluginsConfigRes, err error) { var data = model.PluginsConfigEditInput{} if err = gconv.Scan(req, &data); err != nil { return @@ -64,7 +70,7 @@ func (u *csystempluginsConfig) EditPluginsConfig(ctx context.Context, req *syste } // SavePluginsConfig 修改数据 -func (u *csystempluginsConfig) SavePluginsConfig(ctx context.Context, req *system.SavePluginsConfigReq) (res *system.SavePluginsConfigRes, err error) { +func (u *cSystemPluginsConfig) SavePluginsConfig(ctx context.Context, req *system.SavePluginsConfigReq) (res *system.SavePluginsConfigRes, err error) { var data = model.PluginsConfigAddInput{} if err = gconv.Scan(req, &data); err != nil { return @@ -74,7 +80,7 @@ func (u *csystempluginsConfig) SavePluginsConfig(ctx context.Context, req *syste } // DeletePluginsConfig 删除数据 -func (u *csystempluginsConfig) DeletePluginsConfig(ctx context.Context, req *system.DeletePluginsConfigReq) (res *system.DeletePluginsConfigRes, err error) { +func (u *cSystemPluginsConfig) DeletePluginsConfig(ctx context.Context, req *system.DeletePluginsConfigReq) (res *system.DeletePluginsConfigRes, err error) { if len(req.Ids) == 0 { err = gerror.New("ID参数错误") } diff --git a/internal/controller/system/sys_post.go b/internal/controller/system/sys_post.go index 495ebd1..39ebb2c 100644 --- a/internal/controller/system/sys_post.go +++ b/internal/controller/system/sys_post.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - systemV1 "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + systemV1 "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysPost = cPost{} diff --git a/internal/controller/system/sys_role.go b/internal/controller/system/sys_role.go index 40b5d61..3581e8e 100644 --- a/internal/controller/system/sys_role.go +++ b/internal/controller/system/sys_role.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) // SysRole 角色 diff --git a/internal/controller/system/sys_user.go b/internal/controller/system/sys_user.go index 9968597..0a47a90 100644 --- a/internal/controller/system/sys_user.go +++ b/internal/controller/system/sys_user.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) // SysUser 用户 @@ -14,7 +14,7 @@ var SysUser = cSysUser{} type cSysUser struct{} // UserList 用户列表 -func (u *cSysUser) UserList(ctx context.Context, req *system.UserListReq) (res *system.UserListRes, err error) { +func (c *cSysUser) UserList(ctx context.Context, req *system.UserListReq) (res *system.UserListRes, err error) { //获取所有用户列表 var input *model.UserListDoInput if err = gconv.Scan(req, &input); err != nil { @@ -36,7 +36,7 @@ func (u *cSysUser) UserList(ctx context.Context, req *system.UserListReq) (res * } // AddUser 用户添加 -func (u *cSysUser) AddUser(ctx context.Context, req *system.AddUserReq) (res *system.AddUserRes, err error) { +func (c *cSysUser) AddUser(ctx context.Context, req *system.AddUserReq) (res *system.AddUserRes, err error) { var input *model.AddUserInput if err = gconv.Scan(req, &input); err != nil { return @@ -46,7 +46,7 @@ func (u *cSysUser) AddUser(ctx context.Context, req *system.AddUserReq) (res *sy } // EditUser 用户编辑 -func (u *cSysUser) EditUser(ctx context.Context, req *system.EditUserReq) (res *system.EditUserRes, err error) { +func (c *cSysUser) EditUser(ctx context.Context, req *system.EditUserReq) (res *system.EditUserRes, err error) { var input *model.EditUserInput if err = gconv.Scan(req, &input); err != nil { return @@ -56,7 +56,7 @@ func (u *cSysUser) EditUser(ctx context.Context, req *system.EditUserReq) (res * } // GetUserById 根据ID获取用户信息 -func (u *cSysUser) GetUserById(ctx context.Context, req *system.GetUserByIdReq) (res *system.GetUserByIdRes, err error) { +func (c *cSysUser) GetUserById(ctx context.Context, req *system.GetUserByIdReq) (res *system.GetUserByIdRes, err error) { out, err := service.SysUser().GetUserById(ctx, req.Id) if err != nil { return @@ -74,19 +74,19 @@ func (u *cSysUser) GetUserById(ctx context.Context, req *system.GetUserByIdReq) } // DelUserById 根据ID删除用户 -func (u *cSysUser) DelUserById(ctx context.Context, req *system.DeleteUserByIdReq) (res *system.DeleteUserByIdRes, err error) { +func (c *cSysUser) DelUserById(ctx context.Context, req *system.DeleteUserByIdReq) (res *system.DeleteUserByIdRes, err error) { err = service.SysUser().DelInfoById(ctx, req.Id) return } // ResetPassword 重置密码 -func (u *cSysUser) ResetPassword(ctx context.Context, req *system.ResetPasswordReq) (res *system.ResetPasswordRes, err error) { +func (c *cSysUser) ResetPassword(ctx context.Context, req *system.ResetPasswordReq) (res *system.ResetPasswordRes, err error) { err = service.SysUser().ResetPassword(ctx, req.Id, req.UserPassword) return } // CurrentUser 获取登录用户信息 -func (u *cSysUser) CurrentUser(ctx context.Context, req *system.CurrentUserReq) (res *system.CurrentUserRes, err error) { +func (c *cSysUser) CurrentUser(ctx context.Context, req *system.CurrentUserReq) (res *system.CurrentUserRes, err error) { userInfoOut, menuTreeOur, err := service.SysUser().CurrentUser(ctx) if err != nil { return @@ -111,24 +111,40 @@ func (u *cSysUser) CurrentUser(ctx context.Context, req *system.CurrentUserReq) } // GetParams 获取用户维护相关参数 -func (u *cSysUser) GetParams(ctx context.Context, req *system.UserGetParamsReq) (res *system.UserGetParamsRes, err error) { - res = new(system.UserGetParamsRes) - res.RoleList, err = service.SysRole().GetRoleList(ctx) +func (c *cSysUser) GetParams(ctx context.Context, req *system.UserGetParamsReq) (res *system.UserGetParamsRes, err error) { + rolesOut, err := service.SysRole().GetRoleList(ctx) if err != nil { return } - res.Posts, err = service.SysPost().GetUsedPost(ctx) + var roleList []*model.RoleInfoRes + + if rolesOut != nil { + if err = gconv.Scan(rolesOut, &roleList); err != nil { + return + } + } + var posts []*model.DetailPostRes + postsOut, err := service.SysPost().GetUsedPost(ctx) + if postsOut != nil { + if err = gconv.Scan(postsOut, &posts); err != nil { + return + } + } + res = &system.UserGetParamsRes{ + RoleList: roleList, + Posts: posts, + } return } // EditUserStatus 修改用户状态 -func (u *cSysUser) EditUserStatus(ctx context.Context, req *system.EditUserStatusReq) (res *system.EditUserStatusRes, err error) { +func (c *cSysUser) EditUserStatus(ctx context.Context, req *system.EditUserStatusReq) (res *system.EditUserStatusRes, err error) { err = service.SysUser().EditUserStatus(ctx, req.Id, req.Status) return } // GetUserAll 所有用户列表 -func (u *cSysUser) GetUserAll(ctx context.Context, req *system.GetUserAllReq) (res *system.GetUserAllRes, err error) { +func (c *cSysUser) GetUserAll(ctx context.Context, req *system.GetUserAllReq) (res *system.GetUserAllRes, err error) { //获取所有用户列表 data, err := service.SysUser().GetAll(ctx) var userRes []*model.UserRes @@ -144,7 +160,7 @@ func (u *cSysUser) GetUserAll(ctx context.Context, req *system.GetUserAllReq) (r } // EditUserAvatar 修改用户头像 -func (u *cSysUser) EditUserAvatar(ctx context.Context, req *system.EditUserAvatarReq) (res *system.EditUserAvatarRes, err error) { +func (c *cSysUser) EditUserAvatar(ctx context.Context, req *system.EditUserAvatarReq) (res *system.EditUserAvatarRes, err error) { err = service.SysUser().EditUserAvatar(ctx, req.Id, req.Avatar) return } diff --git a/internal/controller/system/sys_user_online.go b/internal/controller/system/sys_user_online.go index 08a1ec5..2c36363 100644 --- a/internal/controller/system/sys_user_online.go +++ b/internal/controller/system/sys_user_online.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/service" ) var SysUserOnline = cSysUserOnline{} diff --git a/internal/controller/tdengine/td_engine.go b/internal/controller/tdengine/td_engine.go index 0eae4f7..46e4f4d 100644 --- a/internal/controller/tdengine/td_engine.go +++ b/internal/controller/tdengine/td_engine.go @@ -2,8 +2,9 @@ package tdengine import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/tdengine" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/api/v1/tdengine" + "sagooiot/internal/model" + "sagooiot/pkg/tsd" ) var TdEngine = cTdEngine{} @@ -12,7 +13,10 @@ type cTdEngine struct{} // GetTdEngineAllDb 获取所有数据库 func (a *cTdEngine) GetTdEngineAllDb(ctx context.Context, req *tdengine.GetTdEngineAllDbReq) (res *tdengine.GetTdEngineAllDbRes, err error) { - data, err := service.TdEngine().GetTdEngineAllDb(ctx) + tsdDb := tsd.DB() + defer tsdDb.Close() + + data, err := tsdDb.GetAllDatabaseName(ctx) if data != nil { res = &tdengine.GetTdEngineAllDbRes{ Info: data, @@ -23,8 +27,23 @@ func (a *cTdEngine) GetTdEngineAllDb(ctx context.Context, req *tdengine.GetTdEng // GetListTableByDatabases 获取指定数据库下所有的表列表 func (a *cTdEngine) GetListTableByDatabases(ctx context.Context, req *tdengine.GetTdEngineListTableByDatabasesReq) (res *tdengine.GetTdEngineListTableByDatabasesRes, err error) { - data, err := service.TdEngine().GetListTableByDatabases(ctx, req.DbName) - if data != nil { + tsdDb := tsd.DB() + defer tsdDb.Close() + + rs, err := tsdDb.GetTableListByDatabase(ctx, req.DbName) + if err != nil { + return + } + var data []*model.TDEngineTablesList + if len(rs) > 0 { + for _, v := range rs { + data = append(data, &model.TDEngineTablesList{ + DbName: v.DbName, + StableName: v.StableName, + TableName: v.TableName, + CreateTime: v.CreateTime, + }) + } res = &tdengine.GetTdEngineListTableByDatabasesRes{ Info: data, } @@ -34,8 +53,23 @@ func (a *cTdEngine) GetListTableByDatabases(ctx context.Context, req *tdengine.G // GetTdEngineTableInfoByTable 获取指定数据表结构信息 func (a *cTdEngine) GetTdEngineTableInfoByTable(ctx context.Context, req *tdengine.GetTdEngineTableInfoByTableReq) (res *tdengine.GetTdEngineTableInfoByTableRes, err error) { - data, err := service.TdEngine().GetTdEngineTableInfoByTable(ctx, req.DbName, req.TableName) - if data != nil { + tsdDb := tsd.DB() + defer tsdDb.Close() + + rs, err := tsdDb.GetTableInfo(ctx, req.TableName) + if err != nil { + return + } + var data []*model.TDEngineTableInfo + if len(rs) > 0 { + for _, v := range rs { + data = append(data, &model.TDEngineTableInfo{ + Field: v.Field, + Type: v.Type, + Length: v.Length, + Note: v.Note, + }) + } res = &tdengine.GetTdEngineTableInfoByTableRes{ Info: data, } @@ -45,8 +79,19 @@ func (a *cTdEngine) GetTdEngineTableInfoByTable(ctx context.Context, req *tdengi // GetTdEngineTableDataByTable 获取指定表数据信息 func (a *cTdEngine) GetTdEngineTableDataByTable(ctx context.Context, req *tdengine.GetTdEngineTableDataByTableReq) (res *tdengine.GetTdEngineTableDataByTableRes, err error) { - data, err := service.TdEngine().GetTdEngineTableDataByTable(ctx, req.DbName, req.TableName) - if data != nil { + tsdDb := tsd.DB() + defer tsdDb.Close() + + table, err := tsdDb.GetTableData(ctx, req.TableName) + if err != nil { + return + } + var data *model.TableDataInfo + if table != nil { + data = &model.TableDataInfo{ + Filed: table.Filed, + Info: table.Info, + } res = &tdengine.GetTdEngineTableDataByTableRes{ TableDataInfo: data, } diff --git a/internal/controller/thing/overview.go b/internal/controller/thing/overview.go deleted file mode 100644 index 05b004c..0000000 --- a/internal/controller/thing/overview.go +++ /dev/null @@ -1,30 +0,0 @@ -package thing - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/api/v1/thing" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -var DataOverview = cDataOverview{} - -type cDataOverview struct{} - -// 物联概览 -func (a *cDataOverview) ThingOverview(ctx context.Context, req *thing.ThingOverviewReq) (res thing.ThingOverviewRes, err error) { - res.Overview, err = service.DevDevice().Total(ctx) - if err != nil { - return - } - res.Device.MsgTotal, err = service.DevDevice().TotalForMonths(ctx) - if err != nil { - return - } - res.Device.AlarmTotal, err = service.DevDevice().AlarmTotalForMonths(ctx) - if err != nil { - return - } - res.AlarmLevel, err = service.AlarmLog().TotalForLevel(ctx) - - return -} diff --git a/internal/dao/.gitkeep b/internal/dao/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/dao/alarm_level.go b/internal/dao/alarm_level.go index 597c091..8e02319 100644 --- a/internal/dao/alarm_level.go +++ b/internal/dao/alarm_level.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalAlarmLevelDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/alarm_log.go b/internal/dao/alarm_log.go index 0ce64ed..e6bfc8f 100644 --- a/internal/dao/alarm_log.go +++ b/internal/dao/alarm_log.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalAlarmLogDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/alarm_rule.go b/internal/dao/alarm_rule.go index 9f06b15..b0a1bef 100644 --- a/internal/dao/alarm_rule.go +++ b/internal/dao/alarm_rule.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalAlarmRuleDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/base_db_link.go b/internal/dao/base_db_link.go deleted file mode 100644 index a09c095..0000000 --- a/internal/dao/base_db_link.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalBaseDbLinkDao is internal type for wrapping internal DAO implements. -type internalBaseDbLinkDao = *internal.BaseDbLinkDao - -// baseDbLinkDao is the data access object for table base_db_link. -// You can define custom methods on it to extend its functionality as you wish. -type baseDbLinkDao struct { - internalBaseDbLinkDao -} - -var ( - // BaseDbLink is globally public accessible object for table base_db_link operations. - BaseDbLink = baseDbLinkDao{ - internal.NewBaseDbLinkDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/casbin_rule.go b/internal/dao/casbin_rule.go new file mode 100644 index 0000000..90dc5b9 --- /dev/null +++ b/internal/dao/casbin_rule.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalCasbinRuleDao is internal type for wrapping internal DAO implements. +type internalCasbinRuleDao = *internal.CasbinRuleDao + +// casbinRuleDao is the data access object for table casbin_rule. +// You can define custom methods on it to extend its functionality as you wish. +type casbinRuleDao struct { + internalCasbinRuleDao +} + +var ( + // CasbinRule is globally public accessible object for table casbin_rule operations. + CasbinRule = casbinRuleDao{ + internal.NewCasbinRuleDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/city_data.go b/internal/dao/city_data.go deleted file mode 100644 index 5afe0ca..0000000 --- a/internal/dao/city_data.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalCityDataDao is internal type for wrapping internal DAO implements. -type internalCityDataDao = *internal.CityDataDao - -// cityDataDao is the data access object for table city_data. -// You can define custom methods on it to extend its functionality as you wish. -type cityDataDao struct { - internalCityDataDao -} - -var ( - // CityData is globally public accessible object for table city_data operations. - CityData = cityDataDao{ - internal.NewCityDataDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/data_node.go b/internal/dao/data_node.go deleted file mode 100644 index 8cee1ef..0000000 --- a/internal/dao/data_node.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalDataNodeDao is internal type for wrapping internal DAO implements. -type internalDataNodeDao = *internal.DataNodeDao - -// dataNodeDao is the data access object for table data_node. -// You can define custom methods on it to extend its functionality as you wish. -type dataNodeDao struct { - internalDataNodeDao -} - -var ( - // DataNode is globally public accessible object for table data_node operations. - DataNode = dataNodeDao{ - internal.NewDataNodeDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/data_source.go b/internal/dao/data_source.go deleted file mode 100644 index 091e066..0000000 --- a/internal/dao/data_source.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalDataSourceDao is internal type for wrapping internal DAO implements. -type internalDataSourceDao = *internal.DataSourceDao - -// dataSourceDao is the data access object for table data_source. -// You can define custom methods on it to extend its functionality as you wish. -type dataSourceDao struct { - internalDataSourceDao -} - -var ( - // DataSource is globally public accessible object for table data_source operations. - DataSource = dataSourceDao{ - internal.NewDataSourceDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/data_template.go b/internal/dao/data_template.go deleted file mode 100644 index 5a8b654..0000000 --- a/internal/dao/data_template.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalDataTemplateDao is internal type for wrapping internal DAO implements. -type internalDataTemplateDao = *internal.DataTemplateDao - -// dataTemplateDao is the data access object for table data_template. -// You can define custom methods on it to extend its functionality as you wish. -type dataTemplateDao struct { - internalDataTemplateDao -} - -var ( - // DataTemplate is globally public accessible object for table data_template operations. - DataTemplate = dataTemplateDao{ - internal.NewDataTemplateDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/data_template_busi.go b/internal/dao/data_template_busi.go deleted file mode 100644 index 303bd51..0000000 --- a/internal/dao/data_template_busi.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalDataTemplateBusiDao is internal type for wrapping internal DAO implements. -type internalDataTemplateBusiDao = *internal.DataTemplateBusiDao - -// dataTemplateBusiDao is the data access object for table data_template_busi. -// You can define custom methods on it to extend its functionality as you wish. -type dataTemplateBusiDao struct { - internalDataTemplateBusiDao -} - -var ( - // DataTemplateBusi is globally public accessible object for table data_template_busi operations. - DataTemplateBusi = dataTemplateBusiDao{ - internal.NewDataTemplateBusiDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/data_template_node.go b/internal/dao/data_template_node.go deleted file mode 100644 index 8b80247..0000000 --- a/internal/dao/data_template_node.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. -// ================================================================================= - -package dao - -import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" -) - -// internalDataTemplateNodeDao is internal type for wrapping internal DAO implements. -type internalDataTemplateNodeDao = *internal.DataTemplateNodeDao - -// dataTemplateNodeDao is the data access object for table data_template_node. -// You can define custom methods on it to extend its functionality as you wish. -type dataTemplateNodeDao struct { - internalDataTemplateNodeDao -} - -var ( - // DataTemplateNode is globally public accessible object for table data_template_node operations. - DataTemplateNode = dataTemplateNodeDao{ - internal.NewDataTemplateNodeDao(), - } -) - -// Fill with you ideas below. diff --git a/internal/dao/dev_device.go b/internal/dao/dev_device.go index a1c95e6..29bafec 100644 --- a/internal/dao/dev_device.go +++ b/internal/dao/dev_device.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalDevDeviceDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/dev_device_gateway.go b/internal/dao/dev_device_gateway.go new file mode 100644 index 0000000..650cf2d --- /dev/null +++ b/internal/dao/dev_device_gateway.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalDevDeviceGatewayDao is internal type for wrapping internal DAO implements. +type internalDevDeviceGatewayDao = *internal.DevDeviceGatewayDao + +// devDeviceGatewayDao is the data access object for table dev_device_gateway. +// You can define custom methods on it to extend its functionality as you wish. +type devDeviceGatewayDao struct { + internalDevDeviceGatewayDao +} + +var ( + // DevDeviceGateway is globally public accessible object for table dev_device_gateway operations. + DevDeviceGateway = devDeviceGatewayDao{ + internal.NewDevDeviceGatewayDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/dev_device_tag.go b/internal/dao/dev_device_tag.go index 625e7f4..bc9f473 100644 --- a/internal/dao/dev_device_tag.go +++ b/internal/dao/dev_device_tag.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalDevDeviceTagDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/dev_device_tree.go b/internal/dao/dev_device_tree.go new file mode 100644 index 0000000..a568597 --- /dev/null +++ b/internal/dao/dev_device_tree.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalDevDeviceTreeDao is internal type for wrapping internal DAO implements. +type internalDevDeviceTreeDao = *internal.DevDeviceTreeDao + +// devDeviceTreeDao is the data access object for table dev_device_tree. +// You can define custom methods on it to extend its functionality as you wish. +type devDeviceTreeDao struct { + internalDevDeviceTreeDao +} + +var ( + // DevDeviceTree is globally public accessible object for table dev_device_tree operations. + DevDeviceTree = devDeviceTreeDao{ + internal.NewDevDeviceTreeDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/dev_device_tree_info.go b/internal/dao/dev_device_tree_info.go new file mode 100644 index 0000000..5330517 --- /dev/null +++ b/internal/dao/dev_device_tree_info.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalDevDeviceTreeInfoDao is internal type for wrapping internal DAO implements. +type internalDevDeviceTreeInfoDao = *internal.DevDeviceTreeInfoDao + +// devDeviceTreeInfoDao is the data access object for table dev_device_tree_info. +// You can define custom methods on it to extend its functionality as you wish. +type devDeviceTreeInfoDao struct { + internalDevDeviceTreeInfoDao +} + +var ( + // DevDeviceTreeInfo is globally public accessible object for table dev_device_tree_info operations. + DevDeviceTreeInfo = devDeviceTreeInfoDao{ + internal.NewDevDeviceTreeInfoDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/dev_product.go b/internal/dao/dev_product.go index 005fe8e..7408494 100644 --- a/internal/dao/dev_product.go +++ b/internal/dao/dev_product.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalDevProductDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/dev_product_category.go b/internal/dao/dev_product_category.go index f809ca7..9a130ea 100644 --- a/internal/dao/dev_product_category.go +++ b/internal/dao/dev_product_category.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalDevProductCategoryDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/internal/alarm_level.go b/internal/dao/internal/alarm_level.go index 7e801d2..fea019d 100644 --- a/internal/dao/internal/alarm_level.go +++ b/internal/dao/internal/alarm_level.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -70,6 +70,6 @@ func (dao *AlarmLevelDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *AlarmLevelDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *AlarmLevelDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/alarm_log.go b/internal/dao/internal/alarm_log.go index d0336cb..47486c9 100644 --- a/internal/dao/internal/alarm_log.go +++ b/internal/dao/internal/alarm_log.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,16 +21,18 @@ type AlarmLogDao struct { // AlarmLogColumns defines and stores column names for table alarm_log. type AlarmLogColumns struct { Id string // + DeptId string // 部门ID Type string // 告警类型:1=规则告警,2=设备自主告警 RuleId string // 规则id RuleName string // 规则名称 Level string // 告警级别 Data string // 触发告警的数据 + Expression string // 触发告警的表达式 ProductKey string // 产品标识 DeviceKey string // 设备标识 Status string // 告警状态:0=未处理,1=已处理 CreatedAt string // 告警时间 - UpdateBy string // 告警处理人员 + UpdatedBy string // 告警处理人员 UpdatedAt string // 处理时间 Content string // 处理意见 } @@ -38,16 +40,18 @@ type AlarmLogColumns struct { // alarmLogColumns holds the columns for table alarm_log. var alarmLogColumns = AlarmLogColumns{ Id: "id", + DeptId: "dept_id", Type: "type", RuleId: "rule_id", RuleName: "rule_name", Level: "level", Data: "data", + Expression: "expression", ProductKey: "product_key", DeviceKey: "device_key", Status: "status", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", Content: "content", } @@ -92,6 +96,6 @@ func (dao *AlarmLogDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *AlarmLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *AlarmLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/alarm_rule.go b/internal/dao/internal/alarm_rule.go index 4a08638..403673e 100644 --- a/internal/dao/internal/alarm_rule.go +++ b/internal/dao/internal/alarm_rule.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,16 +21,19 @@ type AlarmRuleDao struct { // AlarmRuleColumns defines and stores column names for table alarm_rule. type AlarmRuleColumns struct { Id string // + DeptId string // 部门ID Name string // 告警规则名称 Level string // 告警级别,默认:4(一般) ProductKey string // 产品标识 DeviceKey string // 设备标识 - TriggerType string // 触发类型:1=上线,2=离线,3=属性上报 + TriggerMode string // 触发方式:1=设备触发,2=定时触发 + TriggerType string // 触发类型:1=上线,2=离线,3=属性上报, 4=事件上报 + EventKey string // 事件标识 TriggerCondition string // 触发条件 Action string // 执行动作 Status string // 状态:0=未启用,1=已启用 - CreateBy string // 创建者 - UpdateBy string // 更新者 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 DeletedBy string // 删除者 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 @@ -40,16 +43,19 @@ type AlarmRuleColumns struct { // alarmRuleColumns holds the columns for table alarm_rule. var alarmRuleColumns = AlarmRuleColumns{ Id: "id", + DeptId: "dept_id", Name: "name", Level: "level", ProductKey: "product_key", DeviceKey: "device_key", + TriggerMode: "trigger_mode", TriggerType: "trigger_type", + EventKey: "event_key", TriggerCondition: "trigger_condition", Action: "action", Status: "status", - CreateBy: "create_by", - UpdateBy: "update_by", + CreatedBy: "created_by", + UpdatedBy: "updated_by", DeletedBy: "deleted_by", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -96,6 +102,6 @@ func (dao *AlarmRuleDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *AlarmRuleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *AlarmRuleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/base_db_link.go b/internal/dao/internal/base_db_link.go deleted file mode 100644 index 01e8493..0000000 --- a/internal/dao/internal/base_db_link.go +++ /dev/null @@ -1,103 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// BaseDbLinkDao is the data access object for table base_db_link. -type BaseDbLinkDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns BaseDbLinkColumns // columns contains all the column names of Table for convenient usage. -} - -// BaseDbLinkColumns defines and stores column names for table base_db_link. -type BaseDbLinkColumns struct { - Id string // - Name string // 名称 - Types string // 驱动类型 mysql或oracle - Host string // 主机地址 - Port string // 端口号 - UserName string // 用户名称 - Password string // 密码 - Description string // 描述 - Status string // 状态 0 停用 1启用 - IsDeleted string // 是否删除 0未删除 1已删除 - CreatedBy string // 创建人 - CreatedAt string // 创建时间 - UpdatedBy string // 修改人 - UpdatedAt string // 更新时间 - DeletedBy string // 删除人 - DeletedAt string // 删除时间 -} - -// baseDbLinkColumns holds the columns for table base_db_link. -var baseDbLinkColumns = BaseDbLinkColumns{ - Id: "id", - Name: "name", - Types: "types", - Host: "host", - Port: "port", - UserName: "user_name", - Password: "password", - Description: "description", - Status: "status", - IsDeleted: "is_deleted", - CreatedBy: "created_by", - CreatedAt: "created_at", - UpdatedBy: "updated_by", - UpdatedAt: "updated_at", - DeletedBy: "deleted_by", - DeletedAt: "deleted_at", -} - -// NewBaseDbLinkDao creates and returns a new DAO object for table data access. -func NewBaseDbLinkDao() *BaseDbLinkDao { - return &BaseDbLinkDao{ - group: "default", - table: "base_db_link", - columns: baseDbLinkColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *BaseDbLinkDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *BaseDbLinkDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *BaseDbLinkDao) Columns() BaseDbLinkColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *BaseDbLinkDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *BaseDbLinkDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *BaseDbLinkDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/casbin_rule.go b/internal/dao/internal/casbin_rule.go new file mode 100644 index 0000000..86220b0 --- /dev/null +++ b/internal/dao/internal/casbin_rule.go @@ -0,0 +1,85 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// CasbinRuleDao is the data access object for table casbin_rule. +type CasbinRuleDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns CasbinRuleColumns // columns contains all the column names of Table for convenient usage. +} + +// CasbinRuleColumns defines and stores column names for table casbin_rule. +type CasbinRuleColumns struct { + Ptype string // + V0 string // + V1 string // + V2 string // + V3 string // + V4 string // + V5 string // +} + +// casbinRuleColumns holds the columns for table casbin_rule. +var casbinRuleColumns = CasbinRuleColumns{ + Ptype: "ptype", + V0: "v0", + V1: "v1", + V2: "v2", + V3: "v3", + V4: "v4", + V5: "v5", +} + +// NewCasbinRuleDao creates and returns a new DAO object for table data access. +func NewCasbinRuleDao() *CasbinRuleDao { + return &CasbinRuleDao{ + group: "default", + table: "casbin_rule", + columns: casbinRuleColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *CasbinRuleDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *CasbinRuleDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *CasbinRuleDao) Columns() CasbinRuleColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *CasbinRuleDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *CasbinRuleDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *CasbinRuleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/data_node.go b/internal/dao/internal/data_node.go deleted file mode 100644 index 88a5090..0000000 --- a/internal/dao/internal/data_node.go +++ /dev/null @@ -1,99 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// DataNodeDao is the data access object for table data_node. -type DataNodeDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns DataNodeColumns // columns contains all the column names of Table for convenient usage. -} - -// DataNodeColumns defines and stores column names for table data_node. -type DataNodeColumns struct { - NodeId string // - SourceId string // 数据源ID - Name string // 数据节点名称 - Key string // 数据节点标识 - DataType string // 数据类型 - Value string // 取值项 - IsPk string // 是否主键:0=否,1=是 - Rule string // 规则配置json - CreateBy string // 创建者 - UpdateBy string // 更新者 - DeletedBy string // 删除者 - CreatedAt string // 创建时间 - UpdatedAt string // 更新时间 - DeletedAt string // 删除时间 -} - -// dataNodeColumns holds the columns for table data_node. -var dataNodeColumns = DataNodeColumns{ - NodeId: "node_id", - SourceId: "source_id", - Name: "name", - Key: "key", - DataType: "data_type", - Value: "value", - IsPk: "is_pk", - Rule: "rule", - CreateBy: "create_by", - UpdateBy: "update_by", - DeletedBy: "deleted_by", - CreatedAt: "created_at", - UpdatedAt: "updated_at", - DeletedAt: "deleted_at", -} - -// NewDataNodeDao creates and returns a new DAO object for table data access. -func NewDataNodeDao() *DataNodeDao { - return &DataNodeDao{ - group: "default", - table: "data_node", - columns: dataNodeColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *DataNodeDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *DataNodeDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *DataNodeDao) Columns() DataNodeColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *DataNodeDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *DataNodeDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *DataNodeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/data_source.go b/internal/dao/internal/data_source.go deleted file mode 100644 index 0b47074..0000000 --- a/internal/dao/internal/data_source.go +++ /dev/null @@ -1,103 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// DataSourceDao is the data access object for table data_source. -type DataSourceDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns DataSourceColumns // columns contains all the column names of Table for convenient usage. -} - -// DataSourceColumns defines and stores column names for table data_source. -type DataSourceColumns struct { - SourceId string // - Name string // 数据源名称 - Key string // 数据源标识 - Desc string // 描述 - From string // 数据来源:1=api导入,2=数据库,3=文件,4=设备 - Config string // 数据源配置json:api配置、数据库配置、文件配置 - Rule string // 规则配置json - LockKey string // 锁定key标识:0=未锁定,1=锁定,不允许修改 - Status string // 状态:0=未发布,1=已发布 - DataTable string // 数据表名称 - CreateBy string // 创建者 - UpdateBy string // 更新者 - DeletedBy string // 删除者 - CreatedAt string // 创建时间 - UpdatedAt string // 更新时间 - DeletedAt string // 删除时间 -} - -// dataSourceColumns holds the columns for table data_source. -var dataSourceColumns = DataSourceColumns{ - SourceId: "source_id", - Name: "name", - Key: "key", - Desc: "desc", - From: "from", - Config: "config", - Rule: "rule", - LockKey: "lock_key", - Status: "status", - DataTable: "data_table", - CreateBy: "create_by", - UpdateBy: "update_by", - DeletedBy: "deleted_by", - CreatedAt: "created_at", - UpdatedAt: "updated_at", - DeletedAt: "deleted_at", -} - -// NewDataSourceDao creates and returns a new DAO object for table data access. -func NewDataSourceDao() *DataSourceDao { - return &DataSourceDao{ - group: "default", - table: "data_source", - columns: dataSourceColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *DataSourceDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *DataSourceDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *DataSourceDao) Columns() DataSourceColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *DataSourceDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *DataSourceDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *DataSourceDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/data_template.go b/internal/dao/internal/data_template.go deleted file mode 100644 index 1e80c9d..0000000 --- a/internal/dao/internal/data_template.go +++ /dev/null @@ -1,107 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// DataTemplateDao is the data access object for table data_template. -type DataTemplateDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns DataTemplateColumns // columns contains all the column names of Table for convenient usage. -} - -// DataTemplateColumns defines and stores column names for table data_template. -type DataTemplateColumns struct { - Id string // ID - Name string // 名称 - Key string // 标识 - Desc string // 描述 - Status string // 状态:0=未发布,1=已发布 - CronExpression string // cron执行表达式 - SortNodeKey string // 排序节点标识 - SortDesc string // 排序方式:1=倒序,2=正序 - DataTable string // 数据表名称 - LockKey string // 锁定key标识:0=未锁定,1=锁定,不允许修改 - MainSourceId string // 主数据源 - SourceNodeKey string // 数据源关联节点 - CreateBy string // 创建者 - UpdateBy string // 更新者 - DeletedBy string // 删除者 - CreatedAt string // 创建时间 - UpdatedAt string // 更新时间 - DeletedAt string // 删除时间 -} - -// dataTemplateColumns holds the columns for table data_template. -var dataTemplateColumns = DataTemplateColumns{ - Id: "id", - Name: "name", - Key: "key", - Desc: "desc", - Status: "status", - CronExpression: "cron_expression", - SortNodeKey: "sort_node_key", - SortDesc: "sort_desc", - DataTable: "data_table", - LockKey: "lock_key", - MainSourceId: "main_source_id", - SourceNodeKey: "source_node_key", - CreateBy: "create_by", - UpdateBy: "update_by", - DeletedBy: "deleted_by", - CreatedAt: "created_at", - UpdatedAt: "updated_at", - DeletedAt: "deleted_at", -} - -// NewDataTemplateDao creates and returns a new DAO object for table data access. -func NewDataTemplateDao() *DataTemplateDao { - return &DataTemplateDao{ - group: "default", - table: "data_template", - columns: dataTemplateColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *DataTemplateDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *DataTemplateDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *DataTemplateDao) Columns() DataTemplateColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *DataTemplateDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *DataTemplateDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *DataTemplateDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/data_template_busi.go b/internal/dao/internal/data_template_busi.go deleted file mode 100644 index e4f435c..0000000 --- a/internal/dao/internal/data_template_busi.go +++ /dev/null @@ -1,87 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// DataTemplateBusiDao is the data access object for table data_template_busi. -type DataTemplateBusiDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns DataTemplateBusiColumns // columns contains all the column names of Table for convenient usage. -} - -// DataTemplateBusiColumns defines and stores column names for table data_template_busi. -type DataTemplateBusiColumns struct { - Id string // - DataTemplateId string // 数据建模ID - BusiTypes string // 业务单元 - IsDeleted string // 0未删除 1已删除 - CreatedBy string // 创建人 - CreatedAt string // 创建时间 - DeletedBy string // 删除人 - DeletedAt string // 删除时间 -} - -// dataTemplateBusiColumns holds the columns for table data_template_busi. -var dataTemplateBusiColumns = DataTemplateBusiColumns{ - Id: "id", - DataTemplateId: "data_template_id", - BusiTypes: "busi_types", - IsDeleted: "is_deleted", - CreatedBy: "created_by", - CreatedAt: "created_at", - DeletedBy: "deleted_by", - DeletedAt: "deleted_at", -} - -// NewDataTemplateBusiDao creates and returns a new DAO object for table data access. -func NewDataTemplateBusiDao() *DataTemplateBusiDao { - return &DataTemplateBusiDao{ - group: "default", - table: "data_template_busi", - columns: dataTemplateBusiColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *DataTemplateBusiDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *DataTemplateBusiDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *DataTemplateBusiDao) Columns() DataTemplateBusiColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *DataTemplateBusiDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *DataTemplateBusiDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *DataTemplateBusiDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/data_template_node.go b/internal/dao/internal/data_template_node.go deleted file mode 100644 index 95d27f8..0000000 --- a/internal/dao/internal/data_template_node.go +++ /dev/null @@ -1,107 +0,0 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== - -package internal - -import ( - "context" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -// DataTemplateNodeDao is the data access object for table data_template_node. -type DataTemplateNodeDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns DataTemplateNodeColumns // columns contains all the column names of Table for convenient usage. -} - -// DataTemplateNodeColumns defines and stores column names for table data_template_node. -type DataTemplateNodeColumns struct { - Id string // ID - Tid string // 模型ID - From string // 字段生成方式:1=自动生成,2=数据源 - SourceId string // 数据源ID - NodeId string // 数据源ID - Name string // 节点名称 - Key string // 节点标识 - DataType string // 数据类型 - Default string // 默认值 - Method string // 数值类型,取值方式 - IsPk string // 是否主键:0=否,1=是 - Desc string // 描述 - CreateBy string // 创建者 - UpdateBy string // 更新者 - DeletedBy string // 删除者 - CreatedAt string // 创建时间 - UpdatedAt string // 更新时间 - DeletedAt string // 删除时间 -} - -// dataTemplateNodeColumns holds the columns for table data_template_node. -var dataTemplateNodeColumns = DataTemplateNodeColumns{ - Id: "id", - Tid: "tid", - From: "from", - SourceId: "source_id", - NodeId: "node_id", - Name: "name", - Key: "key", - DataType: "data_type", - Default: "default", - Method: "method", - IsPk: "is_pk", - Desc: "desc", - CreateBy: "create_by", - UpdateBy: "update_by", - DeletedBy: "deleted_by", - CreatedAt: "created_at", - UpdatedAt: "updated_at", - DeletedAt: "deleted_at", -} - -// NewDataTemplateNodeDao creates and returns a new DAO object for table data access. -func NewDataTemplateNodeDao() *DataTemplateNodeDao { - return &DataTemplateNodeDao{ - group: "default", - table: "data_template_node", - columns: dataTemplateNodeColumns, - } -} - -// DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *DataTemplateNodeDao) DB() gdb.DB { - return g.DB(dao.group) -} - -// Table returns the table name of current dao. -func (dao *DataTemplateNodeDao) Table() string { - return dao.table -} - -// Columns returns all column names of current dao. -func (dao *DataTemplateNodeDao) Columns() DataTemplateNodeColumns { - return dao.columns -} - -// Group returns the configuration group name of database of current dao. -func (dao *DataTemplateNodeDao) Group() string { - return dao.group -} - -// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *DataTemplateNodeDao) Ctx(ctx context.Context) *gdb.Model { - return dao.DB().Model(dao.table).Safe().Ctx(ctx) -} - -// Transaction wraps the transaction logic using function f. -// It rollbacks the transaction and returns the error from function f if it returns non-nil error. -// It commits the transaction and returns nil if function f returns nil. -// -// Note that, you should not Commit or Rollback the transaction in function f -// as it is automatically handled by this function. -func (dao *DataTemplateNodeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { - return dao.Ctx(ctx).Transaction(ctx, f) -} diff --git a/internal/dao/internal/dev_device.go b/internal/dao/internal/dev_device.go index a817f2f..7d6839e 100644 --- a/internal/dao/internal/dev_device.go +++ b/internal/dao/internal/dev_device.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,20 +21,27 @@ type DevDeviceDao struct { // DevDeviceColumns defines and stores column names for table dev_device. type DevDeviceColumns struct { Id string // + DeptId string // 部门ID Key string // 设备标识 Name string // 设备名称 - ProductId string // 所属产品 + ProductKey string // 所属产品KEY Desc string // 描述 MetadataTable string // 是否生成物模型子表:0=否,1=是 Status string // 状态:0=未启用,1=离线,2=在线 + OnlineTimeout string // 设备在线超时设置,单位:秒 RegistryTime string // 激活时间 LastOnlineTime string // 最后上线时间 - Certificate string // 设备证书 - SecureKey string // 设备密钥 Version string // 固件版本号 TunnelId string // tunnelId - CreateBy string // 创建者 - UpdateBy string // 更新者 + Lng string // 经度 + Lat string // 纬度 + AuthType string // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser string // 认证用户 + AuthPasswd string // 认证密码 + AccessToken string // AccessToken + CertificateId string // 证书ID + CreatedBy string // 创建者 + UpdatedBy string // 更新者 DeletedBy string // 删除者 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 @@ -44,20 +51,27 @@ type DevDeviceColumns struct { // devDeviceColumns holds the columns for table dev_device. var devDeviceColumns = DevDeviceColumns{ Id: "id", + DeptId: "dept_id", Key: "key", Name: "name", - ProductId: "product_id", + ProductKey: "product_key", Desc: "desc", MetadataTable: "metadata_table", Status: "status", + OnlineTimeout: "online_timeout", RegistryTime: "registry_time", LastOnlineTime: "last_online_time", - Certificate: "certificate", - SecureKey: "secure_key", Version: "version", TunnelId: "tunnel_id", - CreateBy: "create_by", - UpdateBy: "update_by", + Lng: "lng", + Lat: "lat", + AuthType: "auth_type", + AuthUser: "auth_user", + AuthPasswd: "auth_passwd", + AccessToken: "access_token", + CertificateId: "certificate_id", + CreatedBy: "created_by", + UpdatedBy: "updated_by", DeletedBy: "deleted_by", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -104,6 +118,6 @@ func (dao *DevDeviceDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *DevDeviceDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *DevDeviceDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/dev_device_gateway.go b/internal/dao/internal/dev_device_gateway.go new file mode 100644 index 0000000..0ea4147 --- /dev/null +++ b/internal/dao/internal/dev_device_gateway.go @@ -0,0 +1,89 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// DevDeviceGatewayDao is the data access object for table dev_device_gateway. +type DevDeviceGatewayDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns DevDeviceGatewayColumns // columns contains all the column names of Table for convenient usage. +} + +// DevDeviceGatewayColumns defines and stores column names for table dev_device_gateway. +type DevDeviceGatewayColumns struct { + Id string // + GatewayKey string // 网关标识 + SubKey string // 子设备标识 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 + DeletedBy string // 删除者 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 + DeletedAt string // 删除时间 +} + +// devDeviceGatewayColumns holds the columns for table dev_device_gateway. +var devDeviceGatewayColumns = DevDeviceGatewayColumns{ + Id: "id", + GatewayKey: "gateway_key", + SubKey: "sub_key", + CreatedBy: "created_by", + UpdatedBy: "updated_by", + DeletedBy: "deleted_by", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewDevDeviceGatewayDao creates and returns a new DAO object for table data access. +func NewDevDeviceGatewayDao() *DevDeviceGatewayDao { + return &DevDeviceGatewayDao{ + group: "default", + table: "dev_device_gateway", + columns: devDeviceGatewayColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *DevDeviceGatewayDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *DevDeviceGatewayDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *DevDeviceGatewayDao) Columns() DevDeviceGatewayColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *DevDeviceGatewayDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *DevDeviceGatewayDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *DevDeviceGatewayDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/dev_device_tag.go b/internal/dao/internal/dev_device_tag.go index 56fe97c..7b820aa 100644 --- a/internal/dao/internal/dev_device_tag.go +++ b/internal/dao/internal/dev_device_tag.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,29 +21,31 @@ type DevDeviceTagDao struct { // DevDeviceTagColumns defines and stores column names for table dev_device_tag. type DevDeviceTagColumns struct { Id string // + DeptId string // 部门ID DeviceId string // 设备ID DeviceKey string // 设备标识 Key string // 标签标识 Name string // 标签名称 Value string // 标签值 - CreateBy string // 创建者 - UpdateBy string // 更新者 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 DeletedBy string // 删除者 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 DeletedAt string // 删除时间 } -// devDeviceTagColumns holds the columns for table dev_device_tag. +// devDeviceTagColumns holds the columns for table dev_device_tag. var devDeviceTagColumns = DevDeviceTagColumns{ Id: "id", + DeptId: "dept_id", DeviceId: "device_id", DeviceKey: "device_key", Key: "key", Name: "name", Value: "value", - CreateBy: "create_by", - UpdateBy: "update_by", + CreatedBy: "created_by", + UpdatedBy: "updated_by", DeletedBy: "deleted_by", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -90,6 +92,6 @@ func (dao *DevDeviceTagDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *DevDeviceTagDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *DevDeviceTagDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/dev_device_tree.go b/internal/dao/internal/dev_device_tree.go new file mode 100644 index 0000000..fae0c33 --- /dev/null +++ b/internal/dao/internal/dev_device_tree.go @@ -0,0 +1,77 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// DevDeviceTreeDao is the data access object for table dev_device_tree. +type DevDeviceTreeDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns DevDeviceTreeColumns // columns contains all the column names of Table for convenient usage. +} + +// DevDeviceTreeColumns defines and stores column names for table dev_device_tree. +type DevDeviceTreeColumns struct { + Id string // + InfoId string // 设备树信息ID + ParentInfoId string // 父ID +} + +// devDeviceTreeColumns holds the columns for table dev_device_tree. +var devDeviceTreeColumns = DevDeviceTreeColumns{ + Id: "id", + InfoId: "info_id", + ParentInfoId: "parent_info_id", +} + +// NewDevDeviceTreeDao creates and returns a new DAO object for table data access. +func NewDevDeviceTreeDao() *DevDeviceTreeDao { + return &DevDeviceTreeDao{ + group: "default", + table: "dev_device_tree", + columns: devDeviceTreeColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *DevDeviceTreeDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *DevDeviceTreeDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *DevDeviceTreeDao) Columns() DevDeviceTreeColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *DevDeviceTreeDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *DevDeviceTreeDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *DevDeviceTreeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/dev_device_tree_info.go b/internal/dao/internal/dev_device_tree_info.go new file mode 100644 index 0000000..ee58dcc --- /dev/null +++ b/internal/dao/internal/dev_device_tree_info.go @@ -0,0 +1,123 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// DevDeviceTreeInfoDao is the data access object for table dev_device_tree_info. +type DevDeviceTreeInfoDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns DevDeviceTreeInfoColumns // columns contains all the column names of Table for convenient usage. +} + +// DevDeviceTreeInfoColumns defines and stores column names for table dev_device_tree_info. +type DevDeviceTreeInfoColumns struct { + Id string // + DeptId string // 部门ID + Name string // 名称 + Code string // 编码 + DeviceKey string // 设备标识 + Company string // 所属公司 + Area string // 区域 + Address string // 地址 + Lng string // 经度 + Lat string // 纬度 + Contact string // 联系人 + Phone string // 联系电话 + StartDate string // 服务周期:开始日期 + EndDate string // 服务周期:截止日期 + Image string // 图片 + Duration string // 时间窗口值 + TimeUnit string // 时间单位:1=秒,2=分钟,3=小时,4=天 + Template string // 页面模板,默认:default + Category string // 分类 + Types string // 类型 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 + DeletedBy string // 删除者 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 + DeletedAt string // 删除时间 +} + +// devDeviceTreeInfoColumns holds the columns for table dev_device_tree_info. +var devDeviceTreeInfoColumns = DevDeviceTreeInfoColumns{ + Id: "id", + DeptId: "dept_id", + Name: "name", + Code: "code", + DeviceKey: "device_key", + Company: "company", + Area: "area", + Address: "address", + Lng: "lng", + Lat: "lat", + Contact: "contact", + Phone: "phone", + StartDate: "start_date", + EndDate: "end_date", + Image: "image", + Duration: "duration", + TimeUnit: "time_unit", + Template: "template", + Category: "category", + Types: "types", + CreatedBy: "created_by", + UpdatedBy: "updated_by", + DeletedBy: "deleted_by", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewDevDeviceTreeInfoDao creates and returns a new DAO object for table data access. +func NewDevDeviceTreeInfoDao() *DevDeviceTreeInfoDao { + return &DevDeviceTreeInfoDao{ + group: "default", + table: "dev_device_tree_info", + columns: devDeviceTreeInfoColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *DevDeviceTreeInfoDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *DevDeviceTreeInfoDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *DevDeviceTreeInfoDao) Columns() DevDeviceTreeInfoColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *DevDeviceTreeInfoDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *DevDeviceTreeInfoDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *DevDeviceTreeInfoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/dev_product.go b/internal/dao/internal/dev_product.go index 431a251..5bb1c97 100644 --- a/internal/dao/internal/dev_product.go +++ b/internal/dao/internal/dev_product.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,21 +21,28 @@ type DevProductDao struct { // DevProductColumns defines and stores column names for table dev_product. type DevProductColumns struct { Id string // + DeptId string // 部门ID Key string // 产品标识 Name string // 产品名称 CategoryId string // 所属品类 MessageProtocol string // 消息协议 TransportProtocol string // 传输协议: MQTT,COAP,UDP ProtocolId string // 协议id - DeviceType string // 设备类型: 网关,设备 + DeviceType string // 设备类型: 网关,设备,子设备 Desc string // 描述 Icon string // 图片地址 Metadata string // 物模型 MetadataTable string // 是否生成物模型表:0=否,1=是 Policy string // 采集策略 Status string // 发布状态:0=未发布,1=已发布 - CreateBy string // 创建者 - UpdateBy string // 更新者 + AuthType string // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser string // 认证用户 + AuthPasswd string // 认证密码 + AccessToken string // AccessToken + CertificateId string // 证书ID + ScriptInfo string // 脚本信息 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 DeletedBy string // 删除者 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 @@ -45,6 +52,7 @@ type DevProductColumns struct { // devProductColumns holds the columns for table dev_product. var devProductColumns = DevProductColumns{ Id: "id", + DeptId: "dept_id", Key: "key", Name: "name", CategoryId: "category_id", @@ -58,8 +66,14 @@ var devProductColumns = DevProductColumns{ MetadataTable: "metadata_table", Policy: "policy", Status: "status", - CreateBy: "create_by", - UpdateBy: "update_by", + AuthType: "auth_type", + AuthUser: "auth_user", + AuthPasswd: "auth_passwd", + AccessToken: "access_token", + CertificateId: "certificate_id", + ScriptInfo: "script_info", + CreatedBy: "created_by", + UpdatedBy: "updated_by", DeletedBy: "deleted_by", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -106,6 +120,6 @@ func (dao *DevProductDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *DevProductDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *DevProductDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/dev_product_category.go b/internal/dao/internal/dev_product_category.go index c35d1a5..f286152 100644 --- a/internal/dao/internal/dev_product_category.go +++ b/internal/dao/internal/dev_product_category.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,27 +21,31 @@ type DevProductCategoryDao struct { // DevProductCategoryColumns defines and stores column names for table dev_product_category. type DevProductCategoryColumns struct { Id string // + DeptId string // 部门ID ParentId string // 父ID Key string // 分类标识 Name string // 分类名称 + Sort string // 排序 Desc string // 描述 - CreateBy string // 创建者 - UpdateBy string // 更新者 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 DeletedBy string // 删除者 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 DeletedAt string // 删除时间 } -// devProductCategoryColumns holds the columns for table dev_product_category. +// devProductCategoryColumns holds the columns for table dev_product_category. var devProductCategoryColumns = DevProductCategoryColumns{ Id: "id", + DeptId: "dept_id", ParentId: "parent_id", Key: "key", Name: "name", + Sort: "sort", Desc: "desc", - CreateBy: "create_by", - UpdateBy: "update_by", + CreatedBy: "created_by", + UpdatedBy: "updated_by", DeletedBy: "deleted_by", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -88,6 +92,6 @@ func (dao *DevProductCategoryDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *DevProductCategoryDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *DevProductCategoryDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/guestbook.go b/internal/dao/internal/guestbook.go new file mode 100644 index 0000000..61683c8 --- /dev/null +++ b/internal/dao/internal/guestbook.go @@ -0,0 +1,83 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// GuestbookDao is the data access object for table guestbook. +type GuestbookDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns GuestbookColumns // columns contains all the column names of Table for convenient usage. +} + +// GuestbookColumns defines and stores column names for table guestbook. +type GuestbookColumns struct { + Id string // + Title string // 留言标题 + Content string // 留言内容 + Contacts string // 联系人 + Telephone string // 联系方式 + CreatedAt string // 留言时间 +} + +// guestbookColumns holds the columns for table guestbook. +var guestbookColumns = GuestbookColumns{ + Id: "id", + Title: "title", + Content: "content", + Contacts: "contacts", + Telephone: "telephone", + CreatedAt: "created_at", +} + +// NewGuestbookDao creates and returns a new DAO object for table data access. +func NewGuestbookDao() *GuestbookDao { + return &GuestbookDao{ + group: "default", + table: "guestbook", + columns: guestbookColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *GuestbookDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *GuestbookDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *GuestbookDao) Columns() GuestbookColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *GuestbookDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *GuestbookDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *GuestbookDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/network_server.go b/internal/dao/internal/network_server.go index 91e7d6e..18736ff 100644 --- a/internal/dao/internal/network_server.go +++ b/internal/dao/internal/network_server.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -20,36 +20,52 @@ type NetworkServerDao struct { // NetworkServerColumns defines and stores column names for table network_server. type NetworkServerColumns struct { - Id string // - Name string // - Types string // tcp/udp - Addr string // - Register string // 注册包 - Heartbeat string // 心跳包 - Protocol string // 协议 - Devices string // 默认设备 - Status string // - CreatedAt string // - UpdatedAt string // - CreateBy string // - Remark string // 备注 + Id string // + DeptId string // 部门ID + Name string // + Types string // tcp/udp + Addr string // + Register string // 注册包 + Heartbeat string // 心跳包 + Protocol string // 协议 + Devices string // 默认设备 + Status string // + CreatedAt string // + UpdatedAt string // + CreateBy string // + Remark string // 备注 + IsTls string // 开启TLS:1=是,0=否 + AuthType string // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser string // 认证用户 + AuthPasswd string // 认证密码 + AccessToken string // AccessToken + CertificateId string // 证书ID + Stick string // 粘包处理方式 } -// networkServerColumns holds the columns for table network_server. +// networkServerColumns holds the columns for table network_server. var networkServerColumns = NetworkServerColumns{ - Id: "id", - Name: "name", - Types: "types", - Addr: "addr", - Register: "register", - Heartbeat: "heartbeat", - Protocol: "protocol", - Devices: "devices", - Status: "status", - CreatedAt: "created_at", - UpdatedAt: "updated_at", - CreateBy: "create_by", - Remark: "remark", + Id: "id", + DeptId: "dept_id", + Name: "name", + Types: "types", + Addr: "addr", + Register: "register", + Heartbeat: "heartbeat", + Protocol: "protocol", + Devices: "devices", + Status: "status", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + CreateBy: "create_by", + Remark: "remark", + IsTls: "is_tls", + AuthType: "auth_type", + AuthUser: "auth_user", + AuthPasswd: "auth_passwd", + AccessToken: "access_token", + CertificateId: "certificate_id", + Stick: "stick", } // NewNetworkServerDao creates and returns a new DAO object for table data access. @@ -92,6 +108,6 @@ func (dao *NetworkServerDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NetworkServerDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NetworkServerDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/network_tunnel.go b/internal/dao/internal/network_tunnel.go index f8753d3..40f106f 100644 --- a/internal/dao/internal/network_tunnel.go +++ b/internal/dao/internal/network_tunnel.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type NetworkTunnelDao struct { // NetworkTunnelColumns defines and stores column names for table network_tunnel. type NetworkTunnelColumns struct { Id string // + DeptId string // 部门ID ServerId string // 服务ID Name string // Types string // @@ -41,6 +42,7 @@ type NetworkTunnelColumns struct { // networkTunnelColumns holds the columns for table network_tunnel. var networkTunnelColumns = NetworkTunnelColumns{ Id: "id", + DeptId: "dept_id", ServerId: "server_id", Name: "name", Types: "types", @@ -98,6 +100,6 @@ func (dao *NetworkTunnelDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NetworkTunnelDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NetworkTunnelDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/notice_config.go b/internal/dao/internal/notice_config.go index 44044e1..507b2db 100644 --- a/internal/dao/internal/notice_config.go +++ b/internal/dao/internal/notice_config.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type NoticeConfigDao struct { // NoticeConfigColumns defines and stores column names for table notice_config. type NoticeConfigColumns struct { Id string // + DeptId string // 部门ID Title string // SendGateway string // Types string // @@ -30,6 +31,7 @@ type NoticeConfigColumns struct { // noticeConfigColumns holds the columns for table notice_config. var noticeConfigColumns = NoticeConfigColumns{ Id: "id", + DeptId: "dept_id", Title: "title", SendGateway: "send_gateway", Types: "types", @@ -76,6 +78,6 @@ func (dao *NoticeConfigDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NoticeConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NoticeConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/notice_info.go b/internal/dao/internal/notice_info.go index cbb2a6b..428b40a 100644 --- a/internal/dao/internal/notice_info.go +++ b/internal/dao/internal/notice_info.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -94,6 +94,6 @@ func (dao *NoticeInfoDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NoticeInfoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NoticeInfoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/notice_log.go b/internal/dao/internal/notice_log.go index ab40503..57ef987 100644 --- a/internal/dao/internal/notice_log.go +++ b/internal/dao/internal/notice_log.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type NoticeLogDao struct { // NoticeLogColumns defines and stores column names for table notice_log. type NoticeLogColumns struct { Id string // + DeptId string // 部门ID SendGateway string // 通知渠道 TemplateId string // 通知模板ID Addressee string // 收信人列表 @@ -34,6 +35,7 @@ type NoticeLogColumns struct { // noticeLogColumns holds the columns for table notice_log. var noticeLogColumns = NoticeLogColumns{ Id: "id", + DeptId: "dept_id", SendGateway: "send_gateway", TemplateId: "template_id", Addressee: "addressee", @@ -84,6 +86,6 @@ func (dao *NoticeLogDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NoticeLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NoticeLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/notice_template.go b/internal/dao/internal/notice_template.go index 8fa4f20..205d8af 100644 --- a/internal/dao/internal/notice_template.go +++ b/internal/dao/internal/notice_template.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type NoticeTemplateDao struct { // NoticeTemplateColumns defines and stores column names for table notice_template. type NoticeTemplateColumns struct { Id string // + DeptId string // 部门ID ConfigId string // SendGateway string // Code string // @@ -32,6 +33,7 @@ type NoticeTemplateColumns struct { // noticeTemplateColumns holds the columns for table notice_template. var noticeTemplateColumns = NoticeTemplateColumns{ Id: "id", + DeptId: "dept_id", ConfigId: "config_id", SendGateway: "send_gateway", Code: "code", @@ -80,6 +82,6 @@ func (dao *NoticeTemplateDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *NoticeTemplateDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *NoticeTemplateDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_api.go b/internal/dao/internal/sys_api.go index 6ba1102..8a0bb79 100644 --- a/internal/dao/internal/sys_api.go +++ b/internal/dao/internal/sys_api.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -24,13 +24,14 @@ type SysApiColumns struct { ParentId string // Name string // 名称 Types string // 1 分类 2接口 + ApiTypes string // 数据字典维护 Method string // 请求方式(数据字典维护) Address string // 接口地址 Remark string // 备注 Status string // 状态 0 停用 1启用 Sort string // 排序 IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建时间 UpdatedBy string // 更新者 UpdatedAt string // 修改时间 @@ -44,13 +45,14 @@ var sysApiColumns = SysApiColumns{ ParentId: "parent_id", Name: "name", Types: "types", + ApiTypes: "api_types", Method: "method", Address: "address", Remark: "remark", Status: "status", Sort: "sort", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", UpdatedBy: "updated_by", UpdatedAt: "updated_at", @@ -98,6 +100,6 @@ func (dao *SysApiDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysApiDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysApiDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_authorize.go b/internal/dao/internal/sys_authorize.go index d5a4b75..c289654 100644 --- a/internal/dao/internal/sys_authorize.go +++ b/internal/dao/internal/sys_authorize.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -86,6 +86,6 @@ func (dao *SysAuthorizeDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysAuthorizeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysAuthorizeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_certificate.go b/internal/dao/internal/sys_certificate.go new file mode 100644 index 0000000..e40d675 --- /dev/null +++ b/internal/dao/internal/sys_certificate.go @@ -0,0 +1,103 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// SysCertificateDao is the data access object for table sys_certificate. +type SysCertificateDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns SysCertificateColumns // columns contains all the column names of Table for convenient usage. +} + +// SysCertificateColumns defines and stores column names for table sys_certificate. +type SysCertificateColumns struct { + Id string // + DeptId string // 部门ID + Name string // 名称 + Standard string // 证书标准 + FileContent string // 证书文件内容 + PublicKeyContent string // 证书公钥内容 + PrivateKeyContent string // 证书私钥内容 + Description string // 说明 + Status string // 状态 0未启用 1启用 + IsDeleted string // 是否删除 0未删除 1已删除 + CreatedBy string // 创建者 + CreatedAt string // 创建日期 + UpdatedBy string // 修改人 + UpdatedAt string // 更新时间 + DeletedBy string // 删除人 + DeletedAt string // 删除时间 +} + +// sysCertificateColumns holds the columns for table sys_certificate. +var sysCertificateColumns = SysCertificateColumns{ + Id: "id", + DeptId: "dept_id", + Name: "name", + Standard: "standard", + FileContent: "file_content", + PublicKeyContent: "public_key_content", + PrivateKeyContent: "private_key_content", + Description: "description", + Status: "status", + IsDeleted: "is_deleted", + CreatedBy: "created_by", + CreatedAt: "created_at", + UpdatedBy: "updated_by", + UpdatedAt: "updated_at", + DeletedBy: "deleted_by", + DeletedAt: "deleted_at", +} + +// NewSysCertificateDao creates and returns a new DAO object for table data access. +func NewSysCertificateDao() *SysCertificateDao { + return &SysCertificateDao{ + group: "default", + table: "sys_certificate", + columns: sysCertificateColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *SysCertificateDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *SysCertificateDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *SysCertificateDao) Columns() SysCertificateColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *SysCertificateDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *SysCertificateDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *SysCertificateDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/sys_config.go b/internal/dao/internal/sys_config.go index 197a9b0..7251966 100644 --- a/internal/dao/internal/sys_config.go +++ b/internal/dao/internal/sys_config.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,17 +21,17 @@ type SysConfigDao struct { // SysConfigColumns defines and stores column names for table sys_config. type SysConfigColumns struct { ConfigId string // 参数主键 + ModuleClassify string // 所属字典类型数据code ConfigName string // 参数名称 ConfigKey string // 参数键名 ConfigValue string // 参数键值 ConfigType string // 系统内置(1是 2否) - ModuleClassify string // 字典分类编码 Remark string // 备注 Status string // 状态 0 停用 1启用 IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建时间 - UpdateBy string // 更新者 + UpdatedBy string // 更新者 UpdatedAt string // 修改时间 DeletedBy string // 删除人 DeletedAt string // 删除时间 @@ -40,17 +40,17 @@ type SysConfigColumns struct { // sysConfigColumns holds the columns for table sys_config. var sysConfigColumns = SysConfigColumns{ ConfigId: "config_id", + ModuleClassify: "module_classify", ConfigName: "config_name", ConfigKey: "config_key", ConfigValue: "config_value", ConfigType: "config_type", - ModuleClassify: "module_classify", Remark: "remark", Status: "status", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", @@ -96,6 +96,6 @@ func (dao *SysConfigDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_dept.go b/internal/dao/internal/sys_dept.go index 2701389..90202d6 100644 --- a/internal/dao/internal/sys_dept.go +++ b/internal/dao/internal/sys_dept.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -39,7 +39,7 @@ type SysDeptColumns struct { DeletedAt string // 删除时间 } -// sysDeptColumns holds the columns for table sys_dept. +// sysDeptColumns holds the columns for table sys_dept. var sysDeptColumns = SysDeptColumns{ DeptId: "dept_id", OrganizationId: "organization_id", @@ -100,6 +100,6 @@ func (dao *SysDeptDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysDeptDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysDeptDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_dict_data.go b/internal/dao/internal/sys_dict_data.go index 1f8fd1d..dcbcf2d 100644 --- a/internal/dao/internal/sys_dict_data.go +++ b/internal/dao/internal/sys_dict_data.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -31,9 +31,9 @@ type SysDictDataColumns struct { Remark string // 备注 Status string // 状态(0正常 1停用) IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建时间 - UpdateBy string // 更新者 + UpdatedBy string // 更新者 UpdatedAt string // 修改时间 DeletedBy string // 删除人 DeletedAt string // 删除时间 @@ -52,9 +52,9 @@ var sysDictDataColumns = SysDictDataColumns{ Remark: "remark", Status: "status", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", @@ -100,6 +100,6 @@ func (dao *SysDictDataDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysDictDataDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysDictDataDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_dict_type.go b/internal/dao/internal/sys_dict_type.go index 9bfb76c..52ab960 100644 --- a/internal/dao/internal/sys_dict_type.go +++ b/internal/dao/internal/sys_dict_type.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -28,9 +28,9 @@ type SysDictTypeColumns struct { Remark string // 备注 Status string // 状态(0正常 1停用) IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建日期 - UpdateBy string // 更新者 + UpdatedBy string // 更新者 UpdatedAt string // 修改日期 DeletedBy string // 删除人 DeletedAt string // 删除时间 @@ -46,9 +46,9 @@ var sysDictTypeColumns = SysDictTypeColumns{ Remark: "remark", Status: "status", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", @@ -94,6 +94,6 @@ func (dao *SysDictTypeDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysDictTypeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysDictTypeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_job.go b/internal/dao/internal/sys_job.go index 6336c94..61ae0b0 100644 --- a/internal/dao/internal/sys_job.go +++ b/internal/dao/internal/sys_job.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -29,15 +29,15 @@ type SysJobColumns struct { MisfirePolicy string // 计划执行策略(1多次执行 2执行一次) Concurrent string // 是否并发执行(0允许 1禁止) Status string // 状态(0正常 1暂停) - CreateBy string // 创建者 - UpdateBy string // 更新者 + CreatedBy string // 创建者 + UpdatedBy string // 更新者 Remark string // 备注信息 CreatedAt string // 创建时间 UpdatedAt string // 更新时间 DeletedAt string // 删除时间 } -// sysJobColumns holds the columns for table sys_job. +// sysJobColumns holds the columns for table sys_job. var sysJobColumns = SysJobColumns{ JobId: "job_id", JobName: "job_name", @@ -48,8 +48,8 @@ var sysJobColumns = SysJobColumns{ MisfirePolicy: "misfire_policy", Concurrent: "concurrent", Status: "status", - CreateBy: "create_by", - UpdateBy: "update_by", + CreatedBy: "created_by", + UpdatedBy: "updated_by", Remark: "remark", CreatedAt: "created_at", UpdatedAt: "updated_at", @@ -96,6 +96,6 @@ func (dao *SysJobDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysJobDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysJobDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_login_log.go b/internal/dao/internal/sys_login_log.go index b80ed47..c56cc1e 100644 --- a/internal/dao/internal/sys_login_log.go +++ b/internal/dao/internal/sys_login_log.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -26,13 +26,13 @@ type SysLoginLogColumns struct { LoginLocation string // 登录地点 Browser string // 浏览器类型 Os string // 操作系统 - Status string // 登录状态(0成功 1失败) + Status string // 登录状态(0失败 1成功) Msg string // 提示消息 LoginTime string // 登录时间 Module string // 登录模块 } -// sysLoginLogColumns holds the columns for table sys_login_log. +// sysLoginLogColumns holds the columns for table sys_login_log. var sysLoginLogColumns = SysLoginLogColumns{ InfoId: "info_id", LoginName: "login_name", @@ -86,6 +86,6 @@ func (dao *SysLoginLogDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysLoginLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysLoginLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_menu.go b/internal/dao/internal/sys_menu.go index 7a8e53e..0a01eb0 100644 --- a/internal/dao/internal/sys_menu.go +++ b/internal/dao/internal/sys_menu.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -23,7 +23,7 @@ type SysMenuColumns struct { Id string // ParentId string // 父ID Name string // 规则名称 - Title string // 规则名称 + Title string // 菜单名称 Icon string // 图标 Condition string // 条件 Remark string // 备注 @@ -50,7 +50,7 @@ type SysMenuColumns struct { DeletedAt string // 删除时间 } -// sysMenuColumns holds the columns for table sys_menu. +// sysMenuColumns holds the columns for table sys_menu. var sysMenuColumns = SysMenuColumns{ Id: "id", ParentId: "parent_id", @@ -122,6 +122,6 @@ func (dao *SysMenuDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysMenuDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_menu_api.go b/internal/dao/internal/sys_menu_api.go index c849b27..3902df3 100644 --- a/internal/dao/internal/sys_menu_api.go +++ b/internal/dao/internal/sys_menu_api.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -30,7 +30,7 @@ type SysMenuApiColumns struct { DeletedAt string // 删除时间 } -// sysMenuApiColumns holds the columns for table sys_menu_api. +// sysMenuApiColumns holds the columns for table sys_menu_api. var sysMenuApiColumns = SysMenuApiColumns{ Id: "id", MenuId: "menu_id", @@ -82,6 +82,6 @@ func (dao *SysMenuApiDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysMenuApiDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysMenuApiDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_menu_button.go b/internal/dao/internal/sys_menu_button.go index afdc92c..061bde8 100644 --- a/internal/dao/internal/sys_menu_button.go +++ b/internal/dao/internal/sys_menu_button.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -36,7 +36,7 @@ type SysMenuButtonColumns struct { DeletedAt string // 删除时间 } -// sysMenuButtonColumns holds the columns for table sys_menu_button. +// sysMenuButtonColumns holds the columns for table sys_menu_button. var sysMenuButtonColumns = SysMenuButtonColumns{ Id: "id", ParentId: "parent_id", @@ -94,6 +94,6 @@ func (dao *SysMenuButtonDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysMenuButtonDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysMenuButtonDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_menu_column.go b/internal/dao/internal/sys_menu_column.go index 6838ced..155c135 100644 --- a/internal/dao/internal/sys_menu_column.go +++ b/internal/dao/internal/sys_menu_column.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -36,7 +36,7 @@ type SysMenuColumnColumns struct { DeletedAt string // 删除时间 } -// sysMenuColumnColumns holds the columns for table sys_menu_column. +// sysMenuColumnColumns holds the columns for table sys_menu_column. var sysMenuColumnColumns = SysMenuColumnColumns{ Id: "id", ParentId: "parent_id", @@ -94,6 +94,6 @@ func (dao *SysMenuColumnDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysMenuColumnDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysMenuColumnDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/city_data.go b/internal/dao/internal/sys_message.go similarity index 51% rename from internal/dao/internal/city_data.go rename to internal/dao/internal/sys_message.go index 0e39c2c..c253639 100644 --- a/internal/dao/internal/city_data.go +++ b/internal/dao/internal/sys_message.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -11,78 +11,72 @@ import ( "github.com/gogf/gf/v2/frame/g" ) -// CityDataDao is the data access object for table city_data. -type CityDataDao struct { - table string // table is the underlying table name of the DAO. - group string // group is the database configuration group name of current DAO. - columns CityDataColumns // columns contains all the column names of Table for convenient usage. +// SysMessageDao is the data access object for table sys_message. +type SysMessageDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns SysMessageColumns // columns contains all the column names of Table for convenient usage. } -// CityDataColumns defines and stores column names for table city_data. -type CityDataColumns struct { +// SysMessageColumns defines and stores column names for table sys_message. +type SysMessageColumns struct { Id string // - Name string // 名字 - Code string // 编码 - ParentId string // 父ID - Sort string // 排序 - Status string // 状态;0:禁用;1:正常 + Title string // 标题 + Types string // 字典表 + Scope string // 消息范围 + Content string // 内容 IsDeleted string // 是否删除 0未删除 1已删除 CreatedBy string // 创建者 CreatedAt string // 创建日期 - UpdatedBy string // 更新者 - UpdatedAt string // 修改日期 DeletedBy string // 删除人 DeletedAt string // 删除时间 } -// cityDataColumns holds the columns for table city_data. -var cityDataColumns = CityDataColumns{ +// sysMessageColumns holds the columns for table sys_message. +var sysMessageColumns = SysMessageColumns{ Id: "id", - Name: "name", - Code: "code", - ParentId: "parent_id", - Sort: "sort", - Status: "status", + Title: "title", + Types: "types", + Scope: "scope", + Content: "content", IsDeleted: "is_deleted", CreatedBy: "created_by", CreatedAt: "created_at", - UpdatedBy: "updated_by", - UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", } -// NewCityDataDao creates and returns a new DAO object for table data access. -func NewCityDataDao() *CityDataDao { - return &CityDataDao{ +// NewSysMessageDao creates and returns a new DAO object for table data access. +func NewSysMessageDao() *SysMessageDao { + return &SysMessageDao{ group: "default", - table: "city_data", - columns: cityDataColumns, + table: "sys_message", + columns: sysMessageColumns, } } // DB retrieves and returns the underlying raw database management object of current DAO. -func (dao *CityDataDao) DB() gdb.DB { +func (dao *SysMessageDao) DB() gdb.DB { return g.DB(dao.group) } // Table returns the table name of current dao. -func (dao *CityDataDao) Table() string { +func (dao *SysMessageDao) Table() string { return dao.table } // Columns returns all column names of current dao. -func (dao *CityDataDao) Columns() CityDataColumns { +func (dao *SysMessageDao) Columns() SysMessageColumns { return dao.columns } // Group returns the configuration group name of database of current dao. -func (dao *CityDataDao) Group() string { +func (dao *SysMessageDao) Group() string { return dao.group } // Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. -func (dao *CityDataDao) Ctx(ctx context.Context) *gdb.Model { +func (dao *SysMessageDao) Ctx(ctx context.Context) *gdb.Model { return dao.DB().Model(dao.table).Safe().Ctx(ctx) } @@ -92,6 +86,6 @@ func (dao *CityDataDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *CityDataDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysMessageDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_messagereceive.go b/internal/dao/internal/sys_messagereceive.go new file mode 100644 index 0000000..0e4bbdc --- /dev/null +++ b/internal/dao/internal/sys_messagereceive.go @@ -0,0 +1,87 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// SysMessagereceiveDao is the data access object for table sys_messagereceive. +type SysMessagereceiveDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns SysMessagereceiveColumns // columns contains all the column names of Table for convenient usage. +} + +// SysMessagereceiveColumns defines and stores column names for table sys_messagereceive. +type SysMessagereceiveColumns struct { + Id string // + UserId string // 用户ID + MessageId string // 消息ID + IsRead string // 是否已读 0 未读 1已读 + IsPush string // 是否已经推送0 否 1是 + IsDeleted string // 是否删除 0未删除 1已删除 + ReadTime string // 阅读时间 + DeletedAt string // 删除时间 +} + +// sysMessagereceiveColumns holds the columns for table sys_messagereceive. +var sysMessagereceiveColumns = SysMessagereceiveColumns{ + Id: "id", + UserId: "user_id", + MessageId: "message_id", + IsRead: "is_read", + IsPush: "is_push", + IsDeleted: "is_deleted", + ReadTime: "read_time", + DeletedAt: "deleted_at", +} + +// NewSysMessagereceiveDao creates and returns a new DAO object for table data access. +func NewSysMessagereceiveDao() *SysMessagereceiveDao { + return &SysMessagereceiveDao{ + group: "default", + table: "sys_messagereceive", + columns: sysMessagereceiveColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *SysMessagereceiveDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *SysMessagereceiveDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *SysMessagereceiveDao) Columns() SysMessagereceiveColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *SysMessagereceiveDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *SysMessagereceiveDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *SysMessagereceiveDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/sys_notifications.go b/internal/dao/internal/sys_notifications.go index 5781852..b76246c 100644 --- a/internal/dao/internal/sys_notifications.go +++ b/internal/dao/internal/sys_notifications.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -29,7 +29,7 @@ type SysNotificationsColumns struct { Status string // 0,未读,1,已读 } -// sysNotificationsColumns holds the columns for table sys_notifications. +// sysNotificationsColumns holds the columns for table sys_notifications. var sysNotificationsColumns = SysNotificationsColumns{ Id: "id", Title: "title", @@ -80,6 +80,6 @@ func (dao *SysNotificationsDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysNotificationsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysNotificationsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_oper_log.go b/internal/dao/internal/sys_oper_log.go index 6f2b015..f30e484 100644 --- a/internal/dao/internal/sys_oper_log.go +++ b/internal/dao/internal/sys_oper_log.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -33,12 +33,12 @@ type SysOperLogColumns struct { OperLocation string // 操作地点 OperParam string // 请求参数 JsonResult string // 返回参数 - Status string // 操作状态(0正常 1异常) + Status string // 操作状态(0异常 1正常) ErrorMsg string // 错误消息 OperTime string // 操作时间 } -// sysOperLogColumns holds the columns for table sys_oper_log. +// sysOperLogColumns holds the columns for table sys_oper_log. var sysOperLogColumns = SysOperLogColumns{ OperId: "oper_id", Title: "title", @@ -98,6 +98,6 @@ func (dao *SysOperLogDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysOperLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysOperLogDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_organization.go b/internal/dao/internal/sys_organization.go index 67226c6..2fffd61 100644 --- a/internal/dao/internal/sys_organization.go +++ b/internal/dao/internal/sys_organization.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type SysOrganizationDao struct { // SysOrganizationColumns defines and stores column names for table sys_organization. type SysOrganizationColumns struct { Id string // 组织ID + DeptId string // 部门ID ParentId string // 父组织id Ancestors string // 祖级列表 Name string // 组织名称 @@ -42,6 +43,7 @@ type SysOrganizationColumns struct { // sysOrganizationColumns holds the columns for table sys_organization. var sysOrganizationColumns = SysOrganizationColumns{ Id: "id", + DeptId: "dept_id", ParentId: "parent_id", Ancestors: "ancestors", Name: "name", @@ -100,6 +102,6 @@ func (dao *SysOrganizationDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysOrganizationDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysOrganizationDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_plugins.go b/internal/dao/internal/sys_plugins.go index 24768d3..9ed42e4 100644 --- a/internal/dao/internal/sys_plugins.go +++ b/internal/dao/internal/sys_plugins.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -20,29 +20,60 @@ type SysPluginsDao struct { // SysPluginsColumns defines and stores column names for table sys_plugins. type SysPluginsColumns struct { - Id string // ID - Name string // 名称 - Title string // 标题 - Intro string // 介绍 - Version string // 版本 - Status string // 状态 - Types string // 插件类型 - Author string // - StartTime string // + Id string // ID + DeptId string // 部门ID + Types string // 插件与SagooIOT的通信方式 + HandleType string // 功能类型 + Name string // 名称 + Title string // 标题 + Description string // 介绍 + Version string // 版本 + Author string // 作者 + Icon string // 插件图标 + Link string // 插件的网址。指向插件的 github 链接。值应为一个可访问的网址 + Command string // 插件的运行指令 + Args string // 插件的指令参数 + Status string // 状态 0未启用 1启用 + FrontendUi string // 是否有插件页面 + FrontendUrl string // 插件页面地址 + FrontendConfiguration string // 是否显示配置页面 + StartTime string // 启动时间 IsDeleted string // 是否删除 0未删除 1已删除 + CreatedBy string // 创建者 + CreatedAt string // 创建日期 + UpdatedBy string // 修改人 + UpdatedAt string // 更新时间 + DeletedBy string // 删除人 + DeletedAt string // 删除时间 } // sysPluginsColumns holds the columns for table sys_plugins. var sysPluginsColumns = SysPluginsColumns{ - Id: "id", - Name: "name", - Title: "title", - Intro: "intro", - Version: "version", - Status: "status", - Types: "types", - Author: "author", - StartTime: "start_time", + Id: "id", + DeptId: "dept_id", + Types: "types", + HandleType: "handle_type", + Name: "name", + Title: "title", + Description: "description", + Version: "version", + Author: "author", + Icon: "icon", + Link: "link", + Command: "command", + Args: "args", + Status: "status", + FrontendUi: "frontend_ui", + FrontendUrl: "frontend_url", + FrontendConfiguration: "frontend_configuration", + StartTime: "start_time", + IsDeleted: "is_deleted", + CreatedBy: "created_by", + CreatedAt: "created_at", + UpdatedBy: "updated_by", + UpdatedAt: "updated_at", + DeletedBy: "deleted_by", + DeletedAt: "deleted_at", } // NewSysPluginsDao creates and returns a new DAO object for table data access. @@ -85,6 +116,6 @@ func (dao *SysPluginsDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysPluginsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysPluginsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_plugins_config.go b/internal/dao/internal/sys_plugins_config.go index 9f921cf..b84b7c5 100644 --- a/internal/dao/internal/sys_plugins_config.go +++ b/internal/dao/internal/sys_plugins_config.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -76,6 +76,6 @@ func (dao *SysPluginsConfigDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysPluginsConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysPluginsConfigDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_post.go b/internal/dao/internal/sys_post.go index b766fcb..4abca62 100644 --- a/internal/dao/internal/sys_post.go +++ b/internal/dao/internal/sys_post.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type SysPostDao struct { // SysPostColumns defines and stores column names for table sys_post. type SysPostColumns struct { PostId string // 岗位ID + DeptId string // 部门ID ParentId string // 父ID PostCode string // 岗位编码 PostName string // 岗位名称 @@ -36,9 +37,10 @@ type SysPostColumns struct { DeletedAt string // 删除时间 } -// sysPostColumns holds the columns for table sys_post. +// sysPostColumns holds the columns for table sys_post. var sysPostColumns = SysPostColumns{ PostId: "post_id", + DeptId: "dept_id", ParentId: "parent_id", PostCode: "post_code", PostName: "post_name", @@ -94,6 +96,6 @@ func (dao *SysPostDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysPostDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysPostDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_role.go b/internal/dao/internal/sys_role.go index 5801533..6ff44a6 100644 --- a/internal/dao/internal/sys_role.go +++ b/internal/dao/internal/sys_role.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -21,6 +21,7 @@ type SysRoleDao struct { // SysRoleColumns defines and stores column names for table sys_role. type SysRoleColumns struct { Id string // + DeptId string // 部门ID ParentId string // 父ID ListOrder string // 排序 Name string // 角色名称 @@ -28,17 +29,18 @@ type SysRoleColumns struct { Remark string // 备注 Status string // 状态;0:禁用;1:正常 IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建日期 - UpdateBy string // 更新者 + UpdatedBy string // 更新者 UpdatedAt string // 修改日期 DeletedBy string // 删除人 DeletedAt string // 删除时间 } -// sysRoleColumns holds the columns for table sys_role. +// sysRoleColumns holds the columns for table sys_role. var sysRoleColumns = SysRoleColumns{ Id: "id", + DeptId: "dept_id", ParentId: "parent_id", ListOrder: "list_order", Name: "name", @@ -46,9 +48,9 @@ var sysRoleColumns = SysRoleColumns{ Remark: "remark", Status: "status", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", @@ -94,6 +96,6 @@ func (dao *SysRoleDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_role_dept.go b/internal/dao/internal/sys_role_dept.go index b61dbcc..d209777 100644 --- a/internal/dao/internal/sys_role_dept.go +++ b/internal/dao/internal/sys_role_dept.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -24,7 +24,7 @@ type SysRoleDeptColumns struct { DeptId string // 部门ID } -// sysRoleDeptColumns holds the columns for table sys_role_dept. +// sysRoleDeptColumns holds the columns for table sys_role_dept. var sysRoleDeptColumns = SysRoleDeptColumns{ RoleId: "role_id", DeptId: "dept_id", @@ -70,6 +70,6 @@ func (dao *SysRoleDeptDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysRoleDeptDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysRoleDeptDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_user.go b/internal/dao/internal/sys_user.go index fb41dd5..1aeb332 100644 --- a/internal/dao/internal/sys_user.go +++ b/internal/dao/internal/sys_user.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -40,15 +40,15 @@ type SysUserColumns struct { LastLoginTime string // 最后登录时间 Status string // 用户状态;0:禁用,1:正常,2:未验证 IsDeleted string // 是否删除 0未删除 1已删除 - CreateBy string // 创建者 + CreatedBy string // 创建者 CreatedAt string // 创建日期 - UpdateBy string // 更新者 + UpdatedBy string // 更新者 UpdatedAt string // 修改日期 DeletedBy string // 删除人 DeletedAt string // 删除时间 } -// sysUserColumns holds the columns for table sys_user. +// sysUserColumns holds the columns for table sys_user. var sysUserColumns = SysUserColumns{ Id: "id", UserName: "user_name", @@ -70,9 +70,9 @@ var sysUserColumns = SysUserColumns{ LastLoginTime: "last_login_time", Status: "status", IsDeleted: "is_deleted", - CreateBy: "create_by", + CreatedBy: "created_by", CreatedAt: "created_at", - UpdateBy: "update_by", + UpdatedBy: "updated_by", UpdatedAt: "updated_at", DeletedBy: "deleted_by", DeletedAt: "deleted_at", @@ -118,6 +118,6 @@ func (dao *SysUserDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysUserDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_user_online.go b/internal/dao/internal/sys_user_online.go index 741adab..317c808 100644 --- a/internal/dao/internal/sys_user_online.go +++ b/internal/dao/internal/sys_user_online.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -31,7 +31,7 @@ type SysUserOnlineColumns struct { Os string // 操作系统 } -// sysUserOnlineColumns holds the columns for table sys_user_online. +// sysUserOnlineColumns holds the columns for table sys_user_online. var sysUserOnlineColumns = SysUserOnlineColumns{ Id: "id", Uuid: "uuid", @@ -84,6 +84,6 @@ func (dao *SysUserOnlineDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysUserOnlineDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysUserOnlineDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_user_password_history.go b/internal/dao/internal/sys_user_password_history.go new file mode 100644 index 0000000..0e26a3a --- /dev/null +++ b/internal/dao/internal/sys_user_password_history.go @@ -0,0 +1,85 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// SysUserPasswordHistoryDao is the data access object for table sys_user_password_history. +type SysUserPasswordHistoryDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of current DAO. + columns SysUserPasswordHistoryColumns // columns contains all the column names of Table for convenient usage. +} + +// SysUserPasswordHistoryColumns defines and stores column names for table sys_user_password_history. +type SysUserPasswordHistoryColumns struct { + Id string // + UserId string // 用户ID + BeforePassword string // 变更之前密码 + AfterPassword string // 变更之后密码 + ChangeTime string // 变更时间 + CreatedAt string // + CreatedBy string // +} + +// sysUserPasswordHistoryColumns holds the columns for table sys_user_password_history. +var sysUserPasswordHistoryColumns = SysUserPasswordHistoryColumns{ + Id: "id", + UserId: "user_id", + BeforePassword: "before_password", + AfterPassword: "after_password", + ChangeTime: "change_time", + CreatedAt: "created_at", + CreatedBy: "created_by", +} + +// NewSysUserPasswordHistoryDao creates and returns a new DAO object for table data access. +func NewSysUserPasswordHistoryDao() *SysUserPasswordHistoryDao { + return &SysUserPasswordHistoryDao{ + group: "default", + table: "sys_user_password_history", + columns: sysUserPasswordHistoryColumns, + } +} + +// DB retrieves and returns the underlying raw database management object of current DAO. +func (dao *SysUserPasswordHistoryDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of current dao. +func (dao *SysUserPasswordHistoryDao) Table() string { + return dao.table +} + +// Columns returns all column names of current dao. +func (dao *SysUserPasswordHistoryDao) Columns() SysUserPasswordHistoryColumns { + return dao.columns +} + +// Group returns the configuration group name of database of current dao. +func (dao *SysUserPasswordHistoryDao) Group() string { + return dao.group +} + +// Ctx creates and returns the Model for current DAO, It automatically sets the context for current operation. +func (dao *SysUserPasswordHistoryDao) Ctx(ctx context.Context) *gdb.Model { + return dao.DB().Model(dao.table).Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rollbacks the transaction and returns the error from function f if it returns non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note that, you should not Commit or Rollback the transaction in function f +// as it is automatically handled by this function. +func (dao *SysUserPasswordHistoryDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/internal/dao/internal/sys_user_post.go b/internal/dao/internal/sys_user_post.go index cb4a170..c1390f2 100644 --- a/internal/dao/internal/sys_user_post.go +++ b/internal/dao/internal/sys_user_post.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -24,7 +24,7 @@ type SysUserPostColumns struct { PostId string // 岗位ID } -// sysUserPostColumns holds the columns for table sys_user_post. +// sysUserPostColumns holds the columns for table sys_user_post. var sysUserPostColumns = SysUserPostColumns{ UserId: "user_id", PostId: "post_id", @@ -70,6 +70,6 @@ func (dao *SysUserPostDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysUserPostDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysUserPostDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/internal/sys_user_role.go b/internal/dao/internal/sys_user_role.go index d0cb3c6..eafeb75 100644 --- a/internal/dao/internal/sys_user_role.go +++ b/internal/dao/internal/sys_user_role.go @@ -1,5 +1,5 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package internal @@ -24,7 +24,7 @@ type SysUserRoleColumns struct { RoleId string // } -// sysUserRoleColumns holds the columns for table sys_user_role. +// sysUserRoleColumns holds the columns for table sys_user_role. var sysUserRoleColumns = SysUserRoleColumns{ UserId: "user_id", RoleId: "role_id", @@ -70,6 +70,6 @@ func (dao *SysUserRoleDao) Ctx(ctx context.Context) *gdb.Model { // // Note that, you should not Commit or Rollback the transaction in function f // as it is automatically handled by this function. -func (dao *SysUserRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) (err error) { +func (dao *SysUserRoleDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { return dao.Ctx(ctx).Transaction(ctx, f) } diff --git a/internal/dao/network_server.go b/internal/dao/network_server.go index c7c5cb0..39ae5db 100644 --- a/internal/dao/network_server.go +++ b/internal/dao/network_server.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNetworkServerDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/network_tunnel.go b/internal/dao/network_tunnel.go index a86c28e..ad34026 100644 --- a/internal/dao/network_tunnel.go +++ b/internal/dao/network_tunnel.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNetworkTunnelDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/notice_config.go b/internal/dao/notice_config.go index 24837df..22f4733 100644 --- a/internal/dao/notice_config.go +++ b/internal/dao/notice_config.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNoticeConfigDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/notice_info.go b/internal/dao/notice_info.go index 77584cd..1cc3794 100644 --- a/internal/dao/notice_info.go +++ b/internal/dao/notice_info.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNoticeInfoDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/notice_log.go b/internal/dao/notice_log.go index 8782023..1d37a17 100644 --- a/internal/dao/notice_log.go +++ b/internal/dao/notice_log.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNoticeLogDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/notice_template.go b/internal/dao/notice_template.go index 76eff91..99c2a8a 100644 --- a/internal/dao/notice_template.go +++ b/internal/dao/notice_template.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalNoticeTemplateDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_api.go b/internal/dao/sys_api.go index 9485708..96d97a2 100644 --- a/internal/dao/sys_api.go +++ b/internal/dao/sys_api.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysApiDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_authorize.go b/internal/dao/sys_authorize.go index 7b340a2..e9bc269 100644 --- a/internal/dao/sys_authorize.go +++ b/internal/dao/sys_authorize.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysAuthorizeDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_certificate.go b/internal/dao/sys_certificate.go new file mode 100644 index 0000000..fe9bfc6 --- /dev/null +++ b/internal/dao/sys_certificate.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalSysCertificateDao is internal type for wrapping internal DAO implements. +type internalSysCertificateDao = *internal.SysCertificateDao + +// sysCertificateDao is the data access object for table sys_certificate. +// You can define custom methods on it to extend its functionality as you wish. +type sysCertificateDao struct { + internalSysCertificateDao +} + +var ( + // SysCertificate is globally public accessible object for table sys_certificate operations. + SysCertificate = sysCertificateDao{ + internal.NewSysCertificateDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/sys_config.go b/internal/dao/sys_config.go index ac26bf0..a5546af 100644 --- a/internal/dao/sys_config.go +++ b/internal/dao/sys_config.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysConfigDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_dept.go b/internal/dao/sys_dept.go index 0b64899..beb6dcb 100644 --- a/internal/dao/sys_dept.go +++ b/internal/dao/sys_dept.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysDeptDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_dict_data.go b/internal/dao/sys_dict_data.go index d130179..7bc0392 100644 --- a/internal/dao/sys_dict_data.go +++ b/internal/dao/sys_dict_data.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysDictDataDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_dict_type.go b/internal/dao/sys_dict_type.go index 291bc48..e0c5ea3 100644 --- a/internal/dao/sys_dict_type.go +++ b/internal/dao/sys_dict_type.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysDictTypeDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_job.go b/internal/dao/sys_job.go index 068b6b0..df4de5e 100644 --- a/internal/dao/sys_job.go +++ b/internal/dao/sys_job.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysJobDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_login_log.go b/internal/dao/sys_login_log.go index e933962..09f749c 100644 --- a/internal/dao/sys_login_log.go +++ b/internal/dao/sys_login_log.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysLoginLogDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_menu.go b/internal/dao/sys_menu.go index 3ef8f89..4dde050 100644 --- a/internal/dao/sys_menu.go +++ b/internal/dao/sys_menu.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysMenuDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_menu_api.go b/internal/dao/sys_menu_api.go index 2de9297..9c0f6b0 100644 --- a/internal/dao/sys_menu_api.go +++ b/internal/dao/sys_menu_api.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysMenuApiDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_menu_button.go b/internal/dao/sys_menu_button.go index eb83dcb..c5c8fb8 100644 --- a/internal/dao/sys_menu_button.go +++ b/internal/dao/sys_menu_button.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysMenuButtonDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_menu_column.go b/internal/dao/sys_menu_column.go index 5f6dcc4..987803f 100644 --- a/internal/dao/sys_menu_column.go +++ b/internal/dao/sys_menu_column.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysMenuColumnDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_message.go b/internal/dao/sys_message.go new file mode 100644 index 0000000..f30c9e6 --- /dev/null +++ b/internal/dao/sys_message.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalSysMessageDao is internal type for wrapping internal DAO implements. +type internalSysMessageDao = *internal.SysMessageDao + +// sysMessageDao is the data access object for table sys_message. +// You can define custom methods on it to extend its functionality as you wish. +type sysMessageDao struct { + internalSysMessageDao +} + +var ( + // SysMessage is globally public accessible object for table sys_message operations. + SysMessage = sysMessageDao{ + internal.NewSysMessageDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/sys_messagereceive.go b/internal/dao/sys_messagereceive.go new file mode 100644 index 0000000..064913f --- /dev/null +++ b/internal/dao/sys_messagereceive.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalSysMessagereceiveDao is internal type for wrapping internal DAO implements. +type internalSysMessagereceiveDao = *internal.SysMessagereceiveDao + +// sysMessagereceiveDao is the data access object for table sys_messagereceive. +// You can define custom methods on it to extend its functionality as you wish. +type sysMessagereceiveDao struct { + internalSysMessagereceiveDao +} + +var ( + // SysMessagereceive is globally public accessible object for table sys_messagereceive operations. + SysMessagereceive = sysMessagereceiveDao{ + internal.NewSysMessagereceiveDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/sys_notifications.go b/internal/dao/sys_notifications.go index 58e2f71..af174c5 100644 --- a/internal/dao/sys_notifications.go +++ b/internal/dao/sys_notifications.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysNotificationsDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_oper_log.go b/internal/dao/sys_oper_log.go index 798abf4..fa099ed 100644 --- a/internal/dao/sys_oper_log.go +++ b/internal/dao/sys_oper_log.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysOperLogDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_organization.go b/internal/dao/sys_organization.go index b8cffaf..6d4a615 100644 --- a/internal/dao/sys_organization.go +++ b/internal/dao/sys_organization.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysOrganizationDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_plugins.go b/internal/dao/sys_plugins.go index 4f80d32..33ddeea 100644 --- a/internal/dao/sys_plugins.go +++ b/internal/dao/sys_plugins.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysPluginsDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_plugins_config.go b/internal/dao/sys_plugins_config.go index 99fcbd1..ed9fef0 100644 --- a/internal/dao/sys_plugins_config.go +++ b/internal/dao/sys_plugins_config.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysPluginsConfigDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_post.go b/internal/dao/sys_post.go index 62eceda..01499fd 100644 --- a/internal/dao/sys_post.go +++ b/internal/dao/sys_post.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysPostDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_role.go b/internal/dao/sys_role.go index 599985f..476bc32 100644 --- a/internal/dao/sys_role.go +++ b/internal/dao/sys_role.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysRoleDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_role_dept.go b/internal/dao/sys_role_dept.go index ce95807..50a2c35 100644 --- a/internal/dao/sys_role_dept.go +++ b/internal/dao/sys_role_dept.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysRoleDeptDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_user.go b/internal/dao/sys_user.go index 69f16cc..bbc4969 100644 --- a/internal/dao/sys_user.go +++ b/internal/dao/sys_user.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysUserDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_user_online.go b/internal/dao/sys_user_online.go index 874be5d..86e9a78 100644 --- a/internal/dao/sys_user_online.go +++ b/internal/dao/sys_user_online.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysUserOnlineDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_user_password_history.go b/internal/dao/sys_user_password_history.go new file mode 100644 index 0000000..c190b65 --- /dev/null +++ b/internal/dao/sys_user_password_history.go @@ -0,0 +1,27 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package dao + +import ( + "sagooiot/internal/dao/internal" +) + +// internalSysUserPasswordHistoryDao is internal type for wrapping internal DAO implements. +type internalSysUserPasswordHistoryDao = *internal.SysUserPasswordHistoryDao + +// sysUserPasswordHistoryDao is the data access object for table sys_user_password_history. +// You can define custom methods on it to extend its functionality as you wish. +type sysUserPasswordHistoryDao struct { + internalSysUserPasswordHistoryDao +} + +var ( + // SysUserPasswordHistory is globally public accessible object for table sys_user_password_history operations. + SysUserPasswordHistory = sysUserPasswordHistoryDao{ + internal.NewSysUserPasswordHistoryDao(), + } +) + +// Fill with you ideas below. diff --git a/internal/dao/sys_user_post.go b/internal/dao/sys_user_post.go index 188c6d5..34cc23f 100644 --- a/internal/dao/sys_user_post.go +++ b/internal/dao/sys_user_post.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysUserPostDao is internal type for wrapping internal DAO implements. diff --git a/internal/dao/sys_user_role.go b/internal/dao/sys_user_role.go index 1f6dc15..5a3853b 100644 --- a/internal/dao/sys_user_role.go +++ b/internal/dao/sys_user_role.go @@ -5,7 +5,7 @@ package dao import ( - "github.com/sagoo-cloud/sagooiot/internal/dao/internal" + "sagooiot/internal/dao/internal" ) // internalSysUserRoleDao is internal type for wrapping internal DAO implements. diff --git a/internal/logic/alarm/alarm_level.go b/internal/logic/alarm/alarm_level.go index ecb8897..f14b7de 100644 --- a/internal/logic/alarm/alarm_level.go +++ b/internal/logic/alarm/alarm_level.go @@ -2,12 +2,13 @@ package alarm import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sAlarmLevel struct{} @@ -21,14 +22,22 @@ func alarmLevelNew() *sAlarmLevel { } func (s *sAlarmLevel) Detail(ctx context.Context, level uint) (out model.AlarmLevelOutput, err error) { - err = dao.AlarmLevel.Ctx(ctx).Where(dao.AlarmLevel.Columns().Level, level).Scan(&out) + err = dao.AlarmLevel.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: 0, + Name: "AlarmLevelDetail", + Force: false, + }).Where(dao.AlarmLevel.Columns().Level, level).Scan(&out) return } func (s *sAlarmLevel) All(ctx context.Context) (out *model.AlarmLevelListOutput, err error) { var p []*entity.AlarmLevel - err = dao.AlarmLevel.Ctx(ctx).Scan(&p) + err = dao.AlarmLevel.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: 0, + Name: "AlarmLevelAll", + Force: false, + }).Scan(&p) if err != nil || p == nil { return } @@ -49,7 +58,10 @@ func (s *sAlarmLevel) Edit(ctx context.Context, in []*model.AlarmLevelEditInput) if err != nil { return } + _, err = cache.Instance().Remove(ctx, "AlarmLevelAll") + if err != nil { + continue + } } - return } diff --git a/internal/logic/alarm/alarm_log.go b/internal/logic/alarm/alarm_log.go index 86f9fe0..746e09c 100644 --- a/internal/logic/alarm/alarm_log.go +++ b/internal/logic/alarm/alarm_log.go @@ -3,10 +3,17 @@ package alarm import ( "context" "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" + "sagooiot/pkg/cache" "strconv" + "time" "github.com/gogf/gf/v2/frame/g" ) @@ -27,7 +34,31 @@ func (s *sAlarmLog) Detail(ctx context.Context, id uint64) (out *model.AlarmLogO } func (s *sAlarmLog) Add(ctx context.Context, in *model.AlarmLogAddInput) (id uint64, err error) { - rs, err := dao.AlarmLog.Ctx(ctx).Data(in).Insert() + var deptId int + if in.Type != 2 { + var alarmRule *model.AlarmRuleOutput + //获取规则信息 + alarmRule, err = service.AlarmRule().Detail(ctx, in.RuleId) + if err != nil { + return + } + if alarmRule != nil { + deptId = alarmRule.DeptId + } + } + rs, err := dao.AlarmLog.Ctx(ctx).Data(do.AlarmLog{ + DeptId: deptId, + Type: in.Type, + RuleId: in.RuleId, + RuleName: in.RuleName, + Level: in.Level, + Data: in.Data, + ProductKey: in.ProductKey, + DeviceKey: in.DeviceKey, + Status: 0, + CreatedAt: gtime.Now(), + Content: "", + }).Insert() if err != nil { return } @@ -39,12 +70,17 @@ func (s *sAlarmLog) Add(ctx context.Context, in *model.AlarmLogAddInput) (id uin func (s *sAlarmLog) List(ctx context.Context, in *model.AlarmLogListInput) (out *model.AlarmLogListOutput, err error) { out = new(model.AlarmLogListOutput) c := dao.AlarmLog.Columns() - m := dao.AlarmLog.Ctx(ctx).WithAll().OrderDesc(c.Id) + m := dao.AlarmLog.Ctx(ctx).WithAll(). + OrderDesc(c.Id) if len(in.DateRange) > 0 { m = m.WhereBetween(c.CreatedAt, in.DateRange[0], in.DateRange[1]) } + if len(in.Status) > 0 { + m = m.Where(dao.AlarmLog.Columns().Status, in.Status) + } + out.Total, _ = m.Count() out.CurrentPage = in.PageNum err = m.Page(in.PageNum, in.PageSize).Scan(&out.List) @@ -52,25 +88,42 @@ func (s *sAlarmLog) List(ctx context.Context, in *model.AlarmLogListInput) (out } func (s *sAlarmLog) Handle(ctx context.Context, in *model.AlarmLogHandleInput) (err error) { + alarmLog, err := s.Detail(ctx, in.Id) + if err != nil { + return + } + if alarmLog == nil { + return gerror.New("告警日志不存在") + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) c := dao.AlarmLog.Columns() _, err = dao.AlarmLog.Ctx(ctx).Data(g.Map{ - c.Status: in.Status, - c.UpdateBy: loginUserId, - c.Content: in.Content, + c.Status: in.Status, + c.UpdatedBy: loginUserId, + c.Content: in.Content, }).Where(c.Id, in.Id).Update() + //更新缓存 + alarmLog.Status = in.Status + key := consts.DeviceAlarmLogPrefix + alarmLog.ProductKey + alarmLog.DeviceKey + alarmLog.Expression + err = cache.Instance().Set(ctx, key, alarmLog, time.Minute*10) + return } func (s *sAlarmLog) TotalForLevel(ctx context.Context) (total []model.AlarmLogLevelTotal, err error) { - rs, err := dao.AlarmLog.Ctx(ctx).Fields("level, count(*) as num").Group(dao.AlarmLevel.Columns().Level).All() + //TODO 缓存时间需要优化 ==================== + rs, err := dao.AlarmLog.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: time.Second * 1000, + Name: "AlarmLogTotalForLevel", + Force: false, + }).Fields("level, count(*) as num").Group(dao.AlarmLevel.Columns().Level).All() if err != nil || rs.Len() == 0 { return } - level, err := service.AlarmLevel().All(ctx) if err != nil { return @@ -99,7 +152,7 @@ func (s *sAlarmLog) TotalForLevel(ctx context.Context) (total []model.AlarmLogLe return } -//ClearLogByDays 按日期删除日志 +// ClearLogByDays 按日期删除日志 func (s *sAlarmLog) ClearLogByDays(ctx context.Context, days int) (err error) { _, err = dao.AlarmLog.Ctx(ctx).Delete("to_days(now())-to_days(`created_at`) > ?", days+1) return diff --git a/internal/logic/alarm/alarm_log_test.go b/internal/logic/alarm/alarm_log_test.go index 7556916..612f5e1 100644 --- a/internal/logic/alarm/alarm_log_test.go +++ b/internal/logic/alarm/alarm_log_test.go @@ -2,8 +2,8 @@ package alarm import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" diff --git a/internal/logic/alarm/alarm_rule.go b/internal/logic/alarm/alarm_rule.go index df9c04a..390a3bf 100644 --- a/internal/logic/alarm/alarm_rule.go +++ b/internal/logic/alarm/alarm_rule.go @@ -3,12 +3,14 @@ package alarm import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/database/gdb" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/dcache" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -48,34 +50,40 @@ func (s *sAlarmRule) List(ctx context.Context, in *model.AlarmRuleListInput) (ou return } +// TODO 废弃 func (s *sAlarmRule) Cache(ctx context.Context) (rs map[string][]model.AlarmRuleOutput, err error) { - key := "alarm:rule" - tag := "alarm" - value := common.Cache().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { - var list []model.AlarmRuleOutput - err = dao.AlarmRule.Ctx(ctx).WithAll(). - Where(dao.AlarmRule.Columns().Status, 1). - OrderDesc(dao.AlarmRule.Columns().Id). - Scan(&list) - if err != nil || len(list) == 0 { - return - } + var list []model.AlarmRuleOutput + err = dao.AlarmRule.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: 0, + Name: consts.CacheAlarmRule, + Force: false, + }).WithAll(). + Where(dao.AlarmRule.Columns().Status, 1). + Where(dao.AlarmRule.Columns().TriggerMode, 1). + OrderDesc(dao.AlarmRule.Columns().Id). + Scan(&list) + if err != nil || len(list) == 0 { + return + } - rs := make(map[string][]model.AlarmRuleOutput) - for _, v := range list { - if v.TriggerCondition != "" { - err = json.Unmarshal([]byte(v.TriggerCondition), &v.Condition) + rs = make(map[string][]model.AlarmRuleOutput, len(list)) + for _, v := range list { + if v.TriggerCondition != "" { + conditionErr := json.Unmarshal([]byte(v.TriggerCondition), &v.Condition) + if conditionErr != nil { + return nil, err } - if v.Action != "" { - err = json.Unmarshal([]byte(v.Action), &v.PerformAction) + } + if v.Action != "" { + performActionErr := json.Unmarshal([]byte(v.Action), &v.PerformAction) + if err != nil { + return nil, performActionErr } - rs[v.ProductKey] = append(rs[v.ProductKey], v) } - value = rs - return - }, 0, tag) + rs[v.ProductKey] = append(rs[v.ProductKey], v) + } - data := gconv.Map(value) + data := gconv.Map(rs) rs = make(map[string][]model.AlarmRuleOutput, len(data)) for k, v := range data { var t []model.AlarmRuleOutput @@ -85,11 +93,72 @@ func (s *sAlarmRule) Cache(ctx context.Context) (rs map[string][]model.AlarmRule } return } -func (s *sAlarmRule) delCache(ctx context.Context) { - key := "alarm:rule" - common.Cache().Remove(ctx, key) + +// CacheAllAlarmRule 缓存所有的告警规则 +func (s *sAlarmRule) CacheAllAlarmRule(ctx context.Context) (err error) { + var list []model.AlarmRuleOutput + err = dao.AlarmRule.Ctx(ctx).WithAll(). + Where(dao.AlarmRule.Columns().Status, 1). // 启用 + Where(dao.AlarmRule.Columns().TriggerMode, 1). // 设备触发 + OrderDesc(dao.AlarmRule.Columns().Id). + Scan(&list) + if err != nil || len(list) == 0 { + return + } + productList := make(map[string]int) + for _, v := range list { + productList[v.ProductKey] = 1 + } + + rs := make(map[string][]model.AlarmRuleOutput, len(productList)) + for k := range productList { + for _, d := range list { + if d.ProductKey == k { + rs[k] = append(rs[k], d) + } + } + //将告警规则缓存到redis + err := dcache.SetDeviceAlarmRule(ctx, k, rs[k]) + if err != nil { + g.Log().Debug(ctx, "CacheAllAlarmRule Error:", err) + } + } + return +} + +// 缓存单个产品的告警规则 +func (s *sAlarmRule) cacheProductAlarmRuleChange(ctx context.Context, productKey string) (err error) { + var list []model.AlarmRuleOutput + err = dao.AlarmRule.Ctx(ctx).WithAll(). + Where(dao.AlarmRule.Columns().ProductKey, productKey). + Where(dao.AlarmRule.Columns().TriggerMode, 1). // 设备触发 + OrderDesc(dao.AlarmRule.Columns().Id). + Scan(&list) + if err != nil || len(list) == 0 { + return + } + productList := make(map[string]int) + for _, v := range list { + productList[v.ProductKey] = 1 + } + + rs := make(map[string][]model.AlarmRuleOutput, len(productList)) + for k := range productList { + for _, d := range list { + if d.ProductKey == k { + rs[k] = append(rs[k], d) + } + } + //将告警规则缓存到redis + err := dcache.SetDeviceAlarmRule(ctx, k, rs[k]) + if err != nil { + g.Log().Debug(ctx, "CacheAllAlarmRule Error:", err) + } + } + return } +// Detail 获取告警规则详情 func (s *sAlarmRule) Detail(ctx context.Context, id uint64) (out *model.AlarmRuleOutput, err error) { err = dao.AlarmRule.Ctx(ctx).WithAll().Where(dao.AlarmRule.Columns().Id, id).Scan(&out) if err != nil || out == nil { @@ -97,8 +166,18 @@ func (s *sAlarmRule) Detail(ctx context.Context, id uint64) (out *model.AlarmRul } out.TriggerTypeName = model.AlarmTriggerType[out.TriggerType] + // 触发类型为上下线 + if out.TriggerType == consts.AlarmTriggerTypeOnline || out.TriggerType == consts.AlarmTriggerTypeOffline { + out.TriggerCondition = "" + } + if out.TriggerCondition != "" { - err = json.Unmarshal([]byte(out.TriggerCondition), &out.Condition) + switch out.TriggerMode { + case consts.AlarmTriggerModeDevice: + err = json.Unmarshal([]byte(out.TriggerCondition), &out.Condition) + case consts.AlarmTriggerModeCron: + err = json.Unmarshal([]byte(out.TriggerCondition), &out.CronCondition) + } } if out.Action != "" { err = json.Unmarshal([]byte(out.Action), &out.PerformAction) @@ -115,17 +194,52 @@ func (s *sAlarmRule) Add(ctx context.Context, in *model.AlarmRuleAddInput) (err if err != nil { return } - param.CreateBy = uint(loginUserId) - param.Status = 0 - param.TriggerCondition, _ = json.Marshal(in.AlarmTriggerCondition) - param.Action, _ = json.Marshal(in.AlarmPerformAction) - _, err = dao.AlarmRule.Ctx(ctx).Data(param).Insert() + // 触发类型为上下线 + if in.TriggerType == consts.AlarmTriggerTypeOnline || in.TriggerType == consts.AlarmTriggerTypeOffline { + in.AlarmTriggerCondition = model.AlarmTriggerCondition{ + TriggerCondition: []model.AlarmCondition{ + { + Filters: []model.AlarmFilters{ + { + Key: "sysReportTime", + Operator: "gt", + Value: []string{"0"}, + }, + }, + }, + }, + } + } + if param.TriggerCondition, err = json.Marshal(in.AlarmTriggerCondition); err != nil { + return + } + if param.Action, err = json.Marshal(in.AlarmPerformAction); err != nil { + return + } + + _, err = dao.AlarmRule.Ctx(ctx).Data(do.AlarmRule{ + DeptId: service.Context().GetUserDeptId(ctx), + Name: param.Name, + Level: param.Level, + ProductKey: param.ProductKey, + DeviceKey: param.DeviceKey, + TriggerType: param.TriggerType, + EventKey: param.EventKey, + TriggerCondition: param.TriggerCondition, + Action: param.Action, + Status: 0, + CreatedBy: uint(loginUserId), + }).Insert() if err != nil { return } - s.delCache(ctx) + //更新缓存 + err = s.cacheProductAlarmRuleChange(ctx, in.ProductKey) + if err != nil { + return err + } return } @@ -147,39 +261,78 @@ func (s *sAlarmRule) Edit(ctx context.Context, in *model.AlarmRuleEditInput) (er if err != nil { return } - param.UpdateBy = uint(loginUserId) + param.UpdatedBy = uint(loginUserId) param.Id = nil - param.TriggerCondition, _ = json.Marshal(in.AlarmTriggerCondition) - param.Action, _ = json.Marshal(in.AlarmPerformAction) + + // 触发类型为上下线 + if in.TriggerType == consts.AlarmTriggerTypeOnline || in.TriggerType == consts.AlarmTriggerTypeOffline { + in.AlarmTriggerCondition = model.AlarmTriggerCondition{ + TriggerCondition: []model.AlarmCondition{ + { + Filters: []model.AlarmFilters{ + { + Key: "sysReportTime", + Operator: "gt", + Value: []string{"0"}, + }, + }, + }, + }, + } + } + if param.TriggerCondition, err = json.Marshal(in.AlarmTriggerCondition); err != nil { + return + } + if param.Action, err = json.Marshal(in.AlarmPerformAction); err != nil { + return + } _, err = dao.AlarmRule.Ctx(ctx).Data(param).Where(dao.AlarmRule.Columns().Id, in.Id).Update() if err != nil { return } - s.delCache(ctx) + + //更新缓存 + err = s.cacheProductAlarmRuleChange(ctx, p.ProductKey) + if err != nil { + return err + } return } +// Deploy 启用告警规则 func (s *sAlarmRule) Deploy(ctx context.Context, id uint64) (err error) { var p *entity.AlarmRule err = dao.AlarmRule.Ctx(ctx).Where(dao.AlarmRule.Columns().Id, id).Scan(&p) if err != nil { return } - if p == nil || p.Status == model.AlarmRuleStatusOn { + + if p == nil || p.Status == consts.AlarmRuleStatusOn { err = gerror.New("告警规则不存在,或已启用") return } + // 定时触发 + if p.TriggerMode == consts.AlarmTriggerModeCron { + if err = s.start(ctx, id); err != nil { + return + } + } + _, err = dao.AlarmRule.Ctx(ctx). - Data(g.Map{dao.AlarmRule.Columns().Status: model.AlarmRuleStatusOn}). + Data(g.Map{dao.AlarmRule.Columns().Status: consts.AlarmRuleStatusOn}). Where(dao.AlarmRule.Columns().Id, id). Update() if err != nil { return } - s.delCache(ctx) + //更新缓存 + err = s.cacheProductAlarmRuleChange(ctx, p.ProductKey) + if err != nil { + return err + } return } @@ -190,20 +343,31 @@ func (s *sAlarmRule) Undeploy(ctx context.Context, id uint64) (err error) { if err != nil { return } - if p == nil || p.Status == model.AlarmRuleStatusOff { + if p == nil || p.Status == consts.AlarmRuleStatusOff { err = gerror.New("告警规则不存在,或已禁用") return } + // 定时触发 + if p.TriggerMode == consts.AlarmTriggerModeCron { + if err = s.stop(ctx, id); err != nil { + return + } + } + _, err = dao.AlarmRule.Ctx(ctx). - Data(g.Map{dao.AlarmRule.Columns().Status: model.AlarmRuleStatusOff}). + Data(g.Map{dao.AlarmRule.Columns().Status: consts.AlarmRuleStatusOff}). Where(dao.AlarmRule.Columns().Id, id). Update() if err != nil { return } - s.delCache(ctx) + //更新缓存 + err = s.cacheProductAlarmRuleChange(ctx, p.ProductKey) + if err != nil { + return err + } return } @@ -217,7 +381,8 @@ func (s *sAlarmRule) Del(ctx context.Context, id uint64) (err error) { err = gerror.New("告警规则不存在") return } - if p.Status == model.AlarmRuleStatusOn { + + if p.Status == consts.AlarmRuleStatusOn { err = gerror.New("告警规则已启用,请先禁用,再删除") return } @@ -231,50 +396,55 @@ func (s *sAlarmRule) Del(ctx context.Context, id uint64) (err error) { DeletedAt: gtime.Now(), }). Where(dao.AlarmRule.Columns().Id, id). - Where(dao.AlarmRule.Columns().Status, model.AlarmRuleStatusOff). + Where(dao.AlarmRule.Columns().Status, consts.AlarmRuleStatusOff). Unscoped(). Update() if err != nil { return } - s.delCache(ctx) + + //更新缓存 + err = s.cacheProductAlarmRuleChange(ctx, p.ProductKey) + if err != nil { + return err + } return } func (s *sAlarmRule) Operator(ctx context.Context) (out []model.OperatorOutput, err error) { out = []model.OperatorOutput{ - {Title: "等于", Type: model.OperatorEq}, - {Title: "不等于", Type: model.OperatorNe}, - {Title: "大于", Type: model.OperatorGt}, - {Title: "大于等于", Type: model.OperatorGte}, - {Title: "小于", Type: model.OperatorLt}, - {Title: "小于等于", Type: model.OperatorLte}, - {Title: "在...之间", Type: model.OperatorBet}, - {Title: "不在...之间", Type: model.OperatorNbet}, + {Title: "等于", Type: consts.OperatorEq}, + {Title: "不等于", Type: consts.OperatorNe}, + {Title: "大于", Type: consts.OperatorGt}, + {Title: "大于等于", Type: consts.OperatorGte}, + {Title: "小于", Type: consts.OperatorLt}, + {Title: "小于等于", Type: consts.OperatorLte}, + {Title: "在...之间", Type: consts.OperatorBet}, + {Title: "不在...之间", Type: consts.OperatorNbet}, } return } func (s *sAlarmRule) TriggerType(ctx context.Context, productKey string) (out []model.TriggerTypeOutput, err error) { out = []model.TriggerTypeOutput{ - {Title: model.AlarmTriggerType[model.AlarmTriggerTypeOnline], Type: model.AlarmTriggerTypeOnline}, - {Title: model.AlarmTriggerType[model.AlarmTriggerTypeOffline], Type: model.AlarmTriggerTypeOffline}, + {Title: model.AlarmTriggerType[consts.AlarmTriggerTypeOnline], Type: consts.AlarmTriggerTypeOnline}, + {Title: model.AlarmTriggerType[consts.AlarmTriggerTypeOffline], Type: consts.AlarmTriggerTypeOffline}, } - product, err := service.DevProduct().Get(ctx, productKey) + product, err := dcache.GetProductDetailInfo(productKey) if err != nil || product == nil { return } if product.TSL != nil { if len(product.TSL.Properties) > 0 { out = append(out, model.TriggerTypeOutput{ - Title: model.AlarmTriggerType[model.AlarmTriggerTypeProperty], Type: model.AlarmTriggerTypeProperty, + Title: model.AlarmTriggerType[consts.AlarmTriggerTypeProperty], Type: consts.AlarmTriggerTypeProperty, }) } if len(product.TSL.Events) > 0 { out = append(out, model.TriggerTypeOutput{ - Title: model.AlarmTriggerType[model.AlarmTriggerTypeEvent], Type: model.AlarmTriggerTypeEvent, + Title: model.AlarmTriggerType[consts.AlarmTriggerTypeEvent], Type: consts.AlarmTriggerTypeEvent, }) } } @@ -282,29 +452,37 @@ func (s *sAlarmRule) TriggerType(ctx context.Context, productKey string) (out [] return } -func (s *sAlarmRule) TriggerParam(ctx context.Context, productKey string, triggerType int) (out []model.TriggerParamOutput, err error) { - out = []model.TriggerParamOutput{ +func (s *sAlarmRule) TriggerParam(ctx context.Context, productKey string, triggerType int, eventKey ...string) (out []model.TriggerParamOutput, err error) { + if triggerType == consts.AlarmTriggerTypeOnline || triggerType == consts.AlarmTriggerTypeOffline { + return + } + out = []model.TriggerParamOutput{ + // {Title: "系统时间", ParamKey: "sysTime"}, {Title: "上报时间", ParamKey: "sysReportTime"}, } - product, err := service.DevProduct().Get(ctx, productKey) + product, err := service.DevProduct().Detail(ctx, productKey) if err != nil || product == nil { return } if product.TSL != nil { switch { - case triggerType == model.AlarmTriggerTypeProperty && len(product.TSL.Properties) > 0: + case triggerType == consts.AlarmTriggerTypeProperty && len(product.TSL.Properties) > 0: for _, v := range product.TSL.Properties { out = append(out, model.TriggerParamOutput{ Title: v.Name, ParamKey: v.Key, }) } - case triggerType == model.AlarmTriggerTypeEvent && len(product.TSL.Events) > 0: + case triggerType == consts.AlarmTriggerTypeEvent && len(product.TSL.Events) > 0: for _, v := range product.TSL.Events { - out = append(out, model.TriggerParamOutput{ - Title: v.Name, ParamKey: v.Key, - }) + if len(eventKey) > 0 && v.Key == eventKey[0] { + for _, ot := range v.Outputs { + out = append(out, model.TriggerParamOutput{ + Title: ot.Name, ParamKey: ot.Key, + }) + } + } } } } diff --git a/internal/logic/alarm/alarm_rule_action.go b/internal/logic/alarm/alarm_rule_action.go new file mode 100644 index 0000000..0a8840e --- /dev/null +++ b/internal/logic/alarm/alarm_rule_action.go @@ -0,0 +1,199 @@ +package alarm + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/dcache" + "sagooiot/pkg/plugins" + extModel "sagooiot/pkg/plugins/model" + "sagooiot/pkg/utility/notifier" + "sagooiot/pkg/utility/utils" + "strings" + "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// doAction 告警执行动作 +func (s *sAlarmRule) doAction(ctx context.Context, rule model.AlarmRuleOutput, expression string, deviceKey string, param any) { + // 执行告警通知 + s.NoticeAction(ctx, rule, expression, deviceKey, param) +} + +// NoticeAction 执行告警通知 +func (s *sAlarmRule) NoticeAction(ctx context.Context, rule model.AlarmRuleOutput, expression string, deviceKey string, param any) { + nt := notifier.NewNotifier(3 * time.Second) + nt.SetCallbacks( + func(state notifier.State) { + s.notice(ctx, rule, expression, deviceKey, param) + }, + func(state notifier.State) { + s.notice(ctx, rule, expression, deviceKey, param) + }, + func(state notifier.State) { + s.notice(ctx, rule, expression, deviceKey, param) + }, + ) + // 频率控制 + i := 0 + for { + i++ + if i == 6 || i == 12 { + key := consts.DeviceAlarmLogPrefix + rule.ProductKey + deviceKey + expression + alarmLogData, err := cache.Instance().Get(ctx, key) + if err != nil { + g.Log().Errorf(ctx, "告警获取日志 - %s :%s", key, err) + } + if alarmLogData.Val() == nil { + break + } + var alarmLog model.AlarmLogOutput + err = gconv.Scan(alarmLogData.Val(), &alarmLog) + if err != nil { + return + } + + // 告警未处理,触发通知 + if alarmLog.Status == model.AlarmLogStatusUnhandle { + nt.Trigger(true) + } + } + if i == 9 || i == 15 { + key := consts.DeviceAlarmLogPrefix + rule.ProductKey + deviceKey + expression + alarmLogData, err := cache.Instance().Get(ctx, key) + + var alarmLog model.AlarmLogOutput + err = gconv.Scan(alarmLogData.Val(), &alarmLog) + if err != nil { + return + } + + if alarmLog.AlarmLog == nil { + break + } + // 告警已处理或忽略,停止触发通知 + if alarmLog.Status == model.AlarmLogStatusHandle || + alarmLog.Status == model.AlarmLogStatusIgnore { + nt.Trigger(false) + } + } + + time.Sleep(1 * time.Second) + if i > 20 { + break + } + } +} + +// notice 发送告警通知 +func (s *sAlarmRule) notice(ctx context.Context, rule model.AlarmRuleOutput, expression string, deviceKey string, param any) { + if len(rule.PerformAction.Action) == 0 { + return + } + + for _, v := range rule.PerformAction.Action { + if v.NoticeTemplate == "" { + continue + } + + // 获取告警模板 + tpl, err := service.NoticeTemplate().GetNoticeTemplateById(ctx, v.NoticeTemplate) + if err != nil { + g.Log().Errorf(ctx, "告警获取通知模板 - %s :%s", v.NoticeTemplate, err) + continue + } + if tpl == nil { + continue + } + + // 获取告警级别名称 + level, err := service.AlarmLevel().Detail(ctx, rule.Level) + if err != nil { + g.Log().Errorf(ctx, "告警获取级别名称 - %s :%s", v.NoticeTemplate, err) + } + + device, err := dcache.GetDeviceDetailInfo(deviceKey) + if err != nil { + g.Log().Errorf(ctx, "告警获取设备信息 - %s :%s", rule.DeviceKey, err) + } + if device == nil { + return + } + + // 模板数据准备,模板变量替换 + var contentData = make(map[string]any) + contentData["Level"] = level.Name + contentData["ProductName"] = device.ProductName + contentData["ProductKey"] = device.Product.Key + contentData["DeviceName"] = device.Name + contentData["DeviceKey"] = deviceKey + contentData["Rule"] = rule.Name + " " + expression + if param != nil { + for k, v := range gconv.Map(param) { + var valueData model.ReportPropertyNode + err := gconv.Scan(v, &valueData) + if err != nil { + continue + } + contentData[gconv.String(k)] = valueData.Value + contentData[gconv.String(k)+"_time"] = gtime.New(valueData.CreateTime).Format("Y-m-d H:i:s") + } + } + // 模板解析 + content, err := utils.ReplaceTemplate(tpl.Content, contentData) + if err != nil { + g.Log().Errorf(ctx, "告警模板解析 - %s :%s", v.NoticeTemplate, err) + } + + // 告警消息发送 + var msg = extModel.NoticeInfoData{} + msg.TemplateCode = tpl.Code + msg.MsgTitle = tpl.Title + msg.MsgBody = content + + if plugins.GetNoticePlugin() != nil { + var toTag []extModel.NoticeSendObject + for _, u := range v.Addressee { + toTag = append(toTag, extModel.NoticeSendObject{ + Name: tpl.SendGateway, + Value: u, + }) + } + msg.Totag = toTag + noticeStatus := 0 + noticeResMsg := "" + sendRes, err := plugins.GetNoticePlugin().NoticeSend(tpl.SendGateway, msg) + if err != nil { + noticeStatus = 0 + noticeResMsg = err.Error() + g.Log().Errorf(ctx, "告警通知发送 - %s :%s", tpl.SendGateway, err) + } else { + noticeResMsg = sendRes.Message + if sendRes.Code != 0 { //如果发送返回的code不为0,说明发送失败 + noticeStatus = 0 + } else { + noticeStatus = 1 + } + } + + // 通知日志记录 + if err = service.NoticeLog().Add(ctx, &model.NoticeLogAddInput{ + TemplateId: tpl.Id, + SendGateway: tpl.SendGateway, + Addressee: strings.Join(v.Addressee, ""), + Title: tpl.Title, + Content: content, + Status: noticeStatus, + FailMsg: noticeResMsg, + SendTime: gtime.Now(), + }); err != nil { + g.Log().Errorf(ctx, "告警通知日志记录:%v", err) + } + } + } +} diff --git a/internal/logic/alarm/alarm_rule_check.go b/internal/logic/alarm/alarm_rule_check.go index 5fce23c..9cd72b4 100644 --- a/internal/logic/alarm/alarm_rule_check.go +++ b/internal/logic/alarm/alarm_rule_check.go @@ -4,59 +4,66 @@ import ( "context" "encoding/json" "fmt" - "github.com/sagoo-cloud/sagooiot/extend" - extModel "github.com/sagoo-cloud/sagooiot/extend/model" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/notifier" - "github.com/sagoo-cloud/sagooiot/utility/utils" - "strconv" - "time" - "github.com/Knetic/govaluate" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/queues" + "sagooiot/pkg/cache" + "sagooiot/pkg/dcache" + "sagooiot/pkg/iotModel" + "strconv" + "time" ) -// 告警匹配检测 -func (s *sAlarmRule) Check(ctx context.Context, productKey string, deviceKey string, triggerType int, data map[string]any) (err error) { - list, err := s.Cache(ctx) - if err != nil { - g.Log().Errorf(ctx, "告警规则缓存获取 - %s - %s:%s", productKey, deviceKey, err) - return - } - if len(list) == 0 { - return +// Check 告警检测 +func (s *sAlarmRule) Check(ctx context.Context, productKey string, deviceKey string, triggerType int, param any, subKey ...string) (err error) { + //g.Log().Debugf(ctx, "alarm_rule_check: deviceKey(%s), productKey(%s), triggerType(%d), param(%v)", deviceKey, productKey, triggerType, param) + + // 网关子设备 + if len(subKey) > 0 { + skey := subKey[0] + sub, _ := dcache.GetDeviceDetailInfo(skey) + productKey = sub.Product.Key + deviceKey = sub.Key } - pList, ok := list[productKey] - if !ok { + + //重组param数据 + eventKey, data, err := s.getParamData(ctx, param) + if data == nil || err != nil { return } - // 根据触发类型,过滤告警规则 - var rules []model.AlarmRuleOutput - for _, r := range pList { - if r.TriggerType == triggerType && r.DeviceKey == deviceKey { - rules = append(rules, r) - } - } - if len(rules) == 0 { + //获取规则列表 + rules, err := s.getAlarmRuleList(ctx, productKey, deviceKey, triggerType, eventKey) + if len(rules) == 0 || err != nil { return } - logData, err := json.Marshal(data) + logData, err := json.Marshal(param) if err != nil { g.Log().Errorf(ctx, "告警规则数据 - %s - %s:%s", productKey, deviceKey, err) return } // 填充告警触发时间 - if ts, ok := data["ts"]; ok { - if t, err := time.Parse("2006-01-02 15:04:05", ts.(string)); err == nil { - data["ts"] = t.Unix() + if ts, ok := data["CreateTime"]; ok { + switch tt := ts.(type) { + case string: + if t, err := time.Parse("2006-01-02 15:04:05", tt); err == nil { + data["CreateTime"] = t.Unix() + } else { + data["CreateTime"] = time.Now().Unix() + } + case int, int32, int64, float32, float64: + data["CreateTime"] = tt.(int64) } } else { - data["ts"] = time.Now().Unix() + data["CreateTime"] = time.Now().Unix() } for _, r := range rules { @@ -80,64 +87,33 @@ func (s *sAlarmRule) Check(ctx context.Context, productKey string, deviceKey str return } if y, ok := rs.(bool); ok && y { - log := &model.AlarmLogAddInput{ + // 写告警日志 + log := model.AlarmLogAddInput{ Type: 1, RuleId: rule.Id, RuleName: rule.Name, Level: rule.Level, Data: string(logData), + Expression: exp, ProductKey: productKey, DeviceKey: deviceKey, } - logId, err := service.AlarmLog().Add(ctx, log) - if err != nil { - g.Log().Errorf(ctx, "告警日志写入 - %s - %s:%s", productKey, deviceKey, err) + + if log.ProductKey == "" { return } - // 触发告警通知 - nt := notifier.NewNotifier(3 * time.Second) - nt.SetCallbacks( - func(state notifier.State) { - s.doAction(ctx, rule, exp) - }, - func(state notifier.State) { - s.doAction(ctx, rule, exp) - }, - func(state notifier.State) { - s.doAction(ctx, rule, exp) - }) - // 频率控制 - i := 0 - for { - i++ - if i == 6 || i == 12 { - alarmLog, _ := service.AlarmLog().Detail(ctx, logId) - if alarmLog == nil { - break - } - // 告警未处理,触发通知 - if alarmLog.Status == model.AlarmLogStatusUnhandle { - nt.Trigger(true) - } - } - if i == 9 || i == 15 { - alarmLog, _ := service.AlarmLog().Detail(ctx, logId) - if alarmLog == nil { - break - } - // 告警已处理或忽略,停止触发通知 - if alarmLog.Status == model.AlarmLogStatusHandle || - alarmLog.Status == model.AlarmLogStatusIgnore { - nt.Trigger(false) - } - } + // 写入队列 + logData, _ := json.Marshal(log) + err = queues.ScheduledDeviceAlarmLog.Push(ctx, consts.QueueDeviceAlarmLogTopic, logData, 10) - time.Sleep(1 * time.Second) - if i > 20 { - break - } + key := consts.DeviceAlarmLogPrefix + productKey + deviceKey + exp + err = cache.Instance().Set(ctx, key, log, 60*time.Second) + if err != nil { + return } + //告警执行动作 + s.doAction(ctx, rule, exp, deviceKey, param) } } }(r) @@ -145,94 +121,86 @@ func (s *sAlarmRule) Check(ctx context.Context, productKey string, deviceKey str return } -// 告警执行动作 -func (s *sAlarmRule) doAction(ctx context.Context, rule model.AlarmRuleOutput, expression string) { - if len(rule.PerformAction.Action) == 0 { - return - } - - for _, v := range rule.PerformAction.Action { - if v.NoticeTemplate == "" { - continue - } - - // 获取告警模板 - tpl, err := service.NoticeTemplate().GetNoticeTemplateById(ctx, v.NoticeTemplate) - if err != nil { - g.Log().Errorf(ctx, "告警获取通知模板 - %s :%s", v.NoticeTemplate, err) - continue +// getParamData 获取Param数据 eventKey 事件标识 +func (s *sAlarmRule) getParamData(ctx context.Context, param any) (eventKey string, res map[string]any, err error) { + var ( + data = make(map[string]any) // 上传数据 + ) + // 上传数据重组 + switch pd := param.(type) { + case iotModel.ReportPropertyData: + for k, v := range pd { + vv := gconv.String(v.Value) + if gstr.IsNumeric(vv) { + data[k] = gconv.Float64(v.Value) + } else if gt, err := gtime.StrToTime(vv); err == nil { + data[k] = gt.Unix() + } else { + data[k] = v.Value + } + data[k+"_time"] = v.CreateTime } - if tpl == nil { - continue + case iotModel.ReportEventData: + for k, v := range pd.Param.Value { + vv := gconv.String(v) + if gstr.IsNumeric(vv) { + data[k] = gconv.Float64(v) + } else if gt, err := gtime.StrToTime(vv); err == nil { + data[k] = gt.Unix() + } else { + data[k] = v + } } + data["CreateTime"] = pd.Param.CreateTime + eventKey = pd.Key + case iotModel.ReportStatusData: + data["Status"] = pd.Status + data["CreateTime"] = pd.CreateTime + default: + return "", nil, gerror.New("数据格式错误") + } + return eventKey, data, nil +} - // 获取告警级别名称 - level, err := service.AlarmLevel().Detail(ctx, rule.Level) - if err != nil { - g.Log().Errorf(ctx, "告警获取级别名称 - %s :%s", v.NoticeTemplate, err) +// 获取告警规则 +func (s *sAlarmRule) getAlarmRuleList(ctx context.Context, productKey, deviceKey string, triggerType int, eventKey string) (res []model.AlarmRuleOutput, err error) { + // 获取规则列表 + AlarmRuleList := dcache.GetDeviceAlarm(ctx, productKey) + if AlarmRuleList == nil { + return + } + // 根据触发类型,过滤告警规则 + for _, r := range AlarmRuleList { + if r.Status == 0 { + return } - - // 获取设备、产品名称 - var ( - pname string - dname string - ) - d, err := service.DevDevice().Get(ctx, rule.DeviceKey) - if err != nil { - g.Log().Errorf(ctx, "告警获取设备信息 - %s :%s", rule.DeviceKey, err) + if r.TriggerCondition != "" { + conditionErr := json.Unmarshal([]byte(r.TriggerCondition), &r.Condition) + if conditionErr != nil { + return nil, err + } } - if d != nil { - pname = d.Product.Name - dname = d.Name + if r.Action != "" { + performActionErr := json.Unmarshal([]byte(r.Action), &r.PerformAction) + if err != nil { + return nil, performActionErr + } } - - // 模板解析 - content, err := utils.ReplaceTemplate(tpl.Content, map[string]any{ - "Level": level.Name, - "Product": pname, - "Device": dname, - "Rule": rule.Name + " " + expression, - }) - if err != nil { - g.Log().Errorf(ctx, "告警模板解析 - %s :%s", v.NoticeTemplate, err) + if triggerType == consts.AlarmTriggerTypeEvent && eventKey != r.EventKey { + continue } - - // 告警消息发送 - var msg = extModel.NoticeInfoData{} - msg.MsgTitle = tpl.Title - msg.MsgBody = content - - for _, u := range v.Addressee { - if extend.GetNoticePlugin() != nil { - msg.Totag = fmt.Sprintf(`[{"name":"%s","value":"%s"}]`, tpl.SendGateway, u) - noticeStatus := 1 - noticeFail := "" - _, err = extend.GetNoticePlugin().NoticeSend(tpl.SendGateway, msg) - if err != nil { - noticeStatus = 0 - noticeFail = err.Error() - g.Log().Errorf(ctx, "告警通知发送 - %s :%s", tpl.SendGateway, err) - } - - // 通知日志记录 - if err = service.NoticeLog().Add(ctx, &model.NoticeLogAddInput{ - TemplateId: tpl.Id, - SendGateway: tpl.SendGateway, - Addressee: u, - Title: tpl.Title, - Content: content, - Status: noticeStatus, - FailMsg: noticeFail, - SendTime: gtime.Now().String(), - }); err != nil { - g.Log().Errorf(ctx, "告警通知日志记录:%v", err) - } - } + if triggerType != r.TriggerType { + continue + } + if r.DeviceKey == deviceKey || r.DeviceKey == "all" || r.DeviceKey == "" { + res = append(res, r) } } + + return } -// 告警条件表达式生成 +// expression 告警条件表达式 func (s *sAlarmRule) expression(ctx context.Context, rule model.AlarmRuleOutput) string { glen := len(rule.Condition.TriggerCondition) var exp string @@ -240,14 +208,14 @@ func (s *sAlarmRule) expression(ctx context.Context, rule model.AlarmRuleOutput) flen := len(group.Filters) var gexp string for _, v := range group.Filters { - if (v.Operator == model.OperatorBet || - v.Operator == model.OperatorNbet) && len(v.Value) < 2 { + if (v.Operator == consts.OperatorBet || + v.Operator == consts.OperatorNbet) && len(v.Value) < 2 { continue } // 如果条件参数是上报时间,则将参数值转换成时间戳 if v.Key == "sysReportTime" { - v.Key = "ts" + v.Key = "CreateTime" if v.Value[0] != "" { if t, err := time.Parse("2006-01-02 15:04:05", v.Value[0]); err == nil { v.Value[0] = strconv.FormatInt(t.Unix(), 10) @@ -263,21 +231,21 @@ func (s *sAlarmRule) expression(ctx context.Context, rule model.AlarmRuleOutput) // 表达式生成 var fexp string switch v.Operator { - case model.OperatorEq: + case consts.OperatorEq: fexp = fmt.Sprintf("(%s %s %s)", v.Key, "==", v.Value[0]) - case model.OperatorNe: + case consts.OperatorNe: fexp = fmt.Sprintf("(%s %s %s)", v.Key, "!=", v.Value[0]) - case model.OperatorGt: + case consts.OperatorGt: fexp = fmt.Sprintf("(%s %s %s)", v.Key, ">", v.Value[0]) - case model.OperatorGte: + case consts.OperatorGte: fexp = fmt.Sprintf("(%s %s %s)", v.Key, ">=", v.Value[0]) - case model.OperatorLt: + case consts.OperatorLt: fexp = fmt.Sprintf("(%s %s %s)", v.Key, "<", v.Value[0]) - case model.OperatorLte: + case consts.OperatorLte: fexp = fmt.Sprintf("(%s %s %s)", v.Key, "<=", v.Value[0]) - case model.OperatorBet: + case consts.OperatorBet: fexp = fmt.Sprintf("(%s >= %s && %s <= %s)", v.Key, v.Value[0], v.Key, v.Value[1]) - case model.OperatorNbet: + case consts.OperatorNbet: fexp = fmt.Sprintf("(%s < %s && %s > %s)", v.Key, v.Value[0], v.Key, v.Value[1]) } if flen > 1 { diff --git a/internal/logic/alarm/alarm_rule_check_test.go b/internal/logic/alarm/alarm_rule_check_test.go index d0fda22..3eaa616 100644 --- a/internal/logic/alarm/alarm_rule_check_test.go +++ b/internal/logic/alarm/alarm_rule_check_test.go @@ -2,10 +2,13 @@ package alarm import ( "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/notice" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/product" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + _ "sagooiot/internal/logic/context" + _ "sagooiot/internal/logic/notice" + _ "sagooiot/internal/logic/product" + _ "sagooiot/internal/logic/system" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" "time" @@ -16,21 +19,16 @@ import ( func TestCheck(t *testing.T) { productKey := "monipower20221103" deviceKey := "t20221333" - data := map[string]any{ - "ts": gtime.Datetime(), - "va": 92.12, + // param := model.ReportPropertyData{ + // "va": {92.12, gtime.Now().Unix()}, + // } + param := model.ReportStatusData{ + Status: "online", + CreateTime: gtime.Now().Unix(), } - err := service.AlarmRule().Check(context.TODO(), productKey, deviceKey, model.AlarmTriggerTypeProperty, data) + err := service.AlarmRule().Check(context.TODO(), productKey, deviceKey, consts.AlarmTriggerTypeOnline, param) if err != nil { t.Fatal(err) } time.Sleep(10 * time.Second) } - -func TestCache(t *testing.T) { - rs, err := service.AlarmRule().Cache(context.TODO()) - if err != nil { - t.Fatal(err) - } - t.Log(rs) -} diff --git a/internal/logic/alarm/alarm_rule_cron.go b/internal/logic/alarm/alarm_rule_cron.go new file mode 100644 index 0000000..40f70a5 --- /dev/null +++ b/internal/logic/alarm/alarm_rule_cron.go @@ -0,0 +1,135 @@ +package alarm + +import ( + "context" + "encoding/json" + "fmt" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gcron" + "github.com/gogf/gf/v2/util/gconv" +) + +// AddCronRule 添加定时触发规则 +func (s *sAlarmRule) AddCronRule(ctx context.Context, in *model.AlarmCronRuleAddInput) (err error) { + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + triggerCondition, _ := json.Marshal(in.AlarmCronCondition) + action, _ := json.Marshal(in.AlarmPerformAction) + + _, err = dao.AlarmRule.Ctx(ctx).Data(do.AlarmRule{ + Name: in.Name, + Level: in.Level, + ProductKey: in.ProductKey, + DeviceKey: in.DeviceKey, + TriggerMode: 2, + TriggerCondition: triggerCondition, + Action: action, + Status: 0, + CreatedBy: uint(loginUserId), + }).Insert() + if err != nil { + return + } + + return +} + +// EditCronRule 编辑定时触发规则 +func (s *sAlarmRule) EditCronRule(ctx context.Context, in *model.AlarmCronRuleEditInput) (err error) { + p, err := s.Detail(ctx, in.Id) + if err != nil { + return err + } + if p == nil { + err = gerror.New("告警规则不存在") + return + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + var param *do.AlarmRule + err = gconv.Scan(in, ¶m) + if err != nil { + return + } + param.UpdatedBy = uint(loginUserId) + param.Id = nil + param.TriggerCondition, _ = json.Marshal(in.AlarmCronCondition) + param.Action, _ = json.Marshal(in.AlarmPerformAction) + + _, err = dao.AlarmRule.Ctx(ctx).Data(param).Where(dao.AlarmRule.Columns().Id, in.Id).Update() + if err != nil { + return + } + + return +} + +// start 启动定时 +func (s *sAlarmRule) start(ctx context.Context, id uint64) error { + rule, err := s.Detail(ctx, id) + if err != nil { + return err + } + if rule == nil || rule.Status == consts.AlarmRuleStatusOn { + return gerror.New("告警规则不存在,或已启用") + } + if rule.TriggerMode == consts.AlarmTriggerModeDevice { + return nil + } + + expList := rule.CronCondition.CronCondition + if len(expList) == 0 { + return gerror.New("请设置定时触发条件") + } + + for i, ex := range expList { + name := fmt.Sprintf("alarm-cron-%d-%d", id, i+1) + + _, err := gcron.AddSingleton(ctx, ex, func(ctx context.Context) { + // 执行动作:发送告警通知 + s.notice(ctx, *rule, ex, "", nil) + }, name) + if err != nil { + return err + } + + gcron.Start(name) + } + + return nil +} + +// stop 停止定时 +func (s *sAlarmRule) stop(ctx context.Context, id uint64) error { + rule, err := s.Detail(ctx, id) + if err != nil { + return err + } + if rule == nil || rule.Status == consts.AlarmRuleStatusOff { + return gerror.New("告警规则不存在,或已停用") + } + if rule.TriggerMode == consts.AlarmTriggerModeDevice { + return nil + } + + expList := rule.CronCondition.CronCondition + if len(expList) == 0 { + return nil + } + + for i := 0; i < len(expList); i++ { + name := fmt.Sprintf("alarm-cron-%d-%d", id, i+1) + gcron.Remove(name) + } + + return nil +} diff --git a/internal/logic/alarm/alarm_rule_cron_test.go b/internal/logic/alarm/alarm_rule_cron_test.go new file mode 100644 index 0000000..481939a --- /dev/null +++ b/internal/logic/alarm/alarm_rule_cron_test.go @@ -0,0 +1,23 @@ +package alarm + +import ( + "context" + "testing" + "time" +) + +func TestStart(t *testing.T) { + ar := alarmRuleNew() + if err := ar.start(context.TODO(), 18); err != nil { + t.Fatal(err) + } + time.Sleep(30 * time.Second) +} + +func TestStop(t *testing.T) { + ar := alarmRuleNew() + if err := ar.stop(context.TODO(), 18); err != nil { + t.Fatal(err) + } + time.Sleep(30 * time.Second) +} diff --git a/internal/logic/alarm/alarm_rule_test.go b/internal/logic/alarm/alarm_rule_test.go new file mode 100644 index 0000000..1c4fd7c --- /dev/null +++ b/internal/logic/alarm/alarm_rule_test.go @@ -0,0 +1,15 @@ +package alarm + +import ( + "context" + "sagooiot/internal/service" + "testing" +) + +func TestCacheAllAlarmRule(t *testing.T) { + err := service.AlarmRule().CacheAllAlarmRule(context.Background()) + if err != nil { + t.Fatal(err) + } + +} diff --git a/internal/logic/analysis/alarm.go b/internal/logic/analysis/alarm.go new file mode 100644 index 0000000..512c7f6 --- /dev/null +++ b/internal/logic/analysis/alarm.go @@ -0,0 +1,214 @@ +package analysis + +import ( + "context" + "errors" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "time" +) + +type sAnalysisAlarm struct{} + +func init() { + service.RegisterAnalysisAlarm(analysisAlarmNew()) +} +func analysisAlarmNew() *sAnalysisAlarm { + return &sAnalysisAlarm{} +} + +// GetDeviceAlertCountByYearMonth 按年度每月设备告警数统计 +func (s *sAnalysisAlarm) GetDeviceAlertCountByYearMonth(ctx context.Context, year string) (res []model.CountData, err error) { + timeTag := fmt.Sprintf("%s:%s", "year", year) + resData, err := cache.Instance().GetOrSetFunc(ctx, consts.AnalysisAlarmCountPrefix+consts.AlarmMonthsMessageVolume+timeTag, func(ctx context.Context) (value interface{}, err error) { + value, err = s.getAlarmDataCount(ctx, "year", year) + return + }, time.Second*15) + err = gconv.Scan(resData.Val(), &res) + return +} + +// GetDeviceAlertCountByMonthDay 按月度每日设备告警数统计 +func (s *sAnalysisAlarm) GetDeviceAlertCountByMonthDay(ctx context.Context, month string) (res []model.CountData, err error) { + year := time.Now().Year() + timeTag := fmt.Sprintf("%s:%s", gconv.String(year), month) + resData, err := cache.Instance().GetOrSetFunc(ctx, consts.AnalysisAlarmCountPrefix+consts.AlarmMonthsMessageVolume+timeTag, func(ctx context.Context) (value interface{}, err error) { + year := time.Now().Year() + value, err = s.getAlarmDataCount(ctx, "month", gconv.String(year), month) + return + }, time.Hour) + err = gconv.Scan(resData.Val(), &res) + return +} + +// GetDeviceAlertCountByDayHour 按日每小时设备告警数统计 +func (s *sAnalysisAlarm) GetDeviceAlertCountByDayHour(ctx context.Context, day string) (res []model.CountData, err error) { + year := time.Now().Year() + month := time.Now().Month() + timeTag := fmt.Sprintf("%s:%s:%s", gconv.String(year), month, day) + resData, err := cache.Instance().GetOrSetFunc(ctx, consts.AnalysisAlarmCountPrefix+consts.AlarmMonthsMessageVolume+timeTag, func(ctx context.Context) (value interface{}, err error) { + value, err = s.getAlarmDataCount(ctx, "month", gconv.String(year), gconv.String(month), day) + return + }, time.Second*15) + + err = gconv.Scan(resData.Val(), &res) + return +} + +// getAlarmDataCount 统计设备告警数据,dataType:year,month,day ,value:year,month,day +// dataType:year,value:year ,获取到指定年的每个月的统计 +// dataType:month,value:year,month 获取指定的年、月的每天的统计 +// dataType:day,value:year,month,day 获取指定年、月、日的每个小时的统计 +func (s *sAnalysisAlarm) getAlarmDataCount(ctx context.Context, dataType string, value ...string) (res []model.CountData, err error) { + + m := dao.AlarmLog.Ctx(ctx) + deviceKeys := getDeviceKeys(ctx, "") + m = m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys) + + switch dataType { + case "year": + m = m.Fields("month(created_at) as title, count(*) as count"). + Where("year(created_at)=?", value[0]). + Group("month(created_at)") + + case "month": + m = m.Fields("day(created_at) AS title, count(*) as count"). + Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]). + Group("day(created_at)") + + case "day": + m = m.Fields("hour(created_at) as title, count(*) as count"). + Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]). + Where("day(created_at)=?", value[2]). + Group("hour(created_at)") + } + + list, err := m.All() + if err != nil { + return + } + + for _, v := range list { + var countData model.CountData + countData.Title = v["title"].String() + countData.Value = v["count"].Int64() + res = append(res, countData) + } + return +} + +// GetAlarmTotalCount 告警总数统计(当年、当月、当日),dataType :day,month,year ,date:2021 or 01 or21 +func (s *sAnalysisAlarm) GetAlarmTotalCount(ctx context.Context, dataType, date string) (number int64, err error) { + resData, err := cache.Instance().GetOrSetFunc(ctx, consts.AnalysisAlarmCountPrefix+consts.AlarmMonthsMessageVolume+dataType, func(ctx context.Context) (value interface{}, err error) { + year := time.Now().Year() + month := time.Now().Month() + switch dataType { + case "year": + value = s.getAlarmTotalCount(ctx, "year", date) + case "month": + value = s.getAlarmTotalCount(ctx, "month", gconv.String(year), date) + case "day": + value = s.getAlarmTotalCount(ctx, "day", gconv.String(year), gconv.String(month), date) + default: + err = errors.New("参数错误") + } + return + }, time.Minute*1) + number = gconv.Int64(resData) + return +} + +func (s *sAnalysisAlarm) getAlarmTotalCount(ctx context.Context, dataType string, value ...string) (number int64) { + m := dao.AlarmLog.Ctx(ctx) + deviceKeys := getDeviceKeys(ctx, "") + m = m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys) + + switch dataType { + case "year": + m = m.Fields("count(*) as count"). + Where("year(created_at)=?", value[0]) + case "month": + m = m.Fields("count(*) as count"). + //WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]) + case "day": + m = m.Fields("count(*) as count"). + //WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]). + Where("day(created_at)=?", value[2]) + } + res, err := m.One() + if err != nil { + g.Log().Debug(ctx, err.Error()) + return + } + number = res["count"].Int64() + return + +} + +// GetAlarmLevelCount 告警级别统计 +func (s *sAnalysisAlarm) GetAlarmLevelCount(ctx context.Context, dataType, date string) (res []model.CountData, err error) { + timeTag := fmt.Sprintf("%s:%s", dataType, date) + resData, err := cache.Instance().GetOrSetFunc(ctx, consts.AnalysisAlarmCountPrefix+consts.AlarmLevelMessageVolume+timeTag, func(ctx context.Context) (value interface{}, err error) { + year := time.Now().Year() + month := int(time.Now().Month()) + switch dataType { + case "year": + value, err = s.getAlarmLevelCount(ctx, "year", date) + case "month": + value, err = s.getAlarmLevelCount(ctx, "month", gconv.String(year), date) + case "day": + value, err = s.getAlarmLevelCount(ctx, "day", gconv.String(year), gconv.String(month), date) + default: + err = errors.New("参数错误") + } + return + }, time.Second*15) + err = gconv.Scan(resData.Val(), &res) + return +} + +func (s *sAnalysisAlarm) getAlarmLevelCount(ctx context.Context, dataType string, value ...string) (res []model.CountData, err error) { + m := dao.AlarmLog.Ctx(ctx).Fields("level as title,count(*) as count") + deviceKeys := getDeviceKeys(ctx, "") + m = m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys) + switch dataType { + case "year": + //m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + m = m.Where("year(created_at)=?", value[0]) + case "month": + //m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + m = m.Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]) + case "day": + //m.WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + m = m.Where("year(created_at)=?", value[0]). + Where("month(created_at)=?", value[1]). + Where("day(created_at)=?", value[2]) + } + resData, err := m.Group("level").All() + if err != nil { + g.Log().Debug(ctx, err.Error()) + return + } + + for _, v := range resData { + var countData model.CountData + countData.Title = v["title"].String() + countData.Value = v["count"].Int64() + res = append(res, countData) + } + + return +} diff --git a/internal/logic/analysis/alarm_test.go b/internal/logic/analysis/alarm_test.go new file mode 100644 index 0000000..9ea02be --- /dev/null +++ b/internal/logic/analysis/alarm_test.go @@ -0,0 +1,47 @@ +package analysis + +import ( + "context" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "testing" + + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" +) + +// 按年统计月度的告警数量 +func TestGetDeviceAlertCountStatsByYearMonth(t *testing.T) { + cache.SetAdapter(context.Background()) + data, err := service.AnalysisAlarm().GetDeviceAlertCountByYearMonth(context.Background(), "2023") + if err != nil { + t.Error(err) + } + t.Log(data) +} + +// 告警按等级统计 +func TestAlarmLevelStats(t *testing.T) { + cache.SetAdapter(context.Background()) + + //指定年份的告警 + data, err := service.AnalysisAlarm().GetAlarmLevelCount(context.Background(), "year", "2023") + if err != nil { + t.Error(err) + } + t.Log(data) + + //本年度指定月份的 + data1, err := service.AnalysisAlarm().GetAlarmLevelCount(context.Background(), "month", "1") + if err != nil { + t.Error(err) + } + t.Log(data1) + + //本月指定天的 + data2, err := service.AnalysisAlarm().GetAlarmLevelCount(context.Background(), "day", "11") + if err != nil { + t.Error(err) + } + t.Log(data2) + +} diff --git a/internal/logic/analysis/base.go b/internal/logic/analysis/base.go new file mode 100644 index 0000000..ef1957b --- /dev/null +++ b/internal/logic/analysis/base.go @@ -0,0 +1,30 @@ +package analysis + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/dao" + "sagooiot/internal/model/entity" +) + +// getDeviceKeys 获取所有设备的key,用于过滤数据权限 +func getDeviceKeys(ctx context.Context, productKey string) (deviceKeys []string) { + var deviceList []*entity.DevDevice + m := dao.DevDevice.Ctx(ctx) + + if productKey != "" { + m = m.Where(dao.DevDevice.Columns().ProductKey, productKey) + } + err := m.Scan(&deviceList) + if err != nil { + g.Log().Debug(ctx, err.Error()) + return + } + if err != nil { + return + } + for _, device := range deviceList { + deviceKeys = append(deviceKeys, device.Key) + } + return +} diff --git a/internal/logic/analysis/device.go b/internal/logic/analysis/device.go new file mode 100644 index 0000000..7f9bad9 --- /dev/null +++ b/internal/logic/analysis/device.go @@ -0,0 +1,191 @@ +package analysis + +import ( + "context" + "errors" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/dcache" + "sagooiot/pkg/utility/utils" + "time" +) + +type sAnalysisDevice struct { +} + +func init() { + service.RegisterAnalysisDevice(analysisDeviceNew()) +} + +func analysisDeviceNew() *sAnalysisDevice { + return &sAnalysisDevice{} +} + +// GetDeviceDataTotalCount 获取设备消息总数统计,dataType :day,month,year +func (s *sAnalysisDevice) GetDeviceDataTotalCount(ctx context.Context, dataType string) (number int64, err error) { + switch dataType { + case "day": + number = GetDeviceDataCountByToday() + case "month": + number = s.getDeviceDataCountTotal(ctx, "month") + case "year": + number = s.getDeviceDataCountTotal(ctx, "year") + default: + err = errors.New("dateType参数错误") + } + return +} + +// GetDeviceOnlineOfflineCount 获取设备在线离线统计 +func (s *sAnalysisDevice) GetDeviceOnlineOfflineCount(ctx context.Context) (res model.DeviceOnlineOfflineCount, err error) { + //设备总量 + total, _ := cache.Instance().Get(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceTotal) + if total.Val() != nil { + res.Total = total.Int() + } else { + m := dao.DevDevice.Ctx(ctx) + // 设备总量 + allNum, _ := m.Count() + _ = cache.Instance().Set(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceTotal, allNum, 0) + res.Total = allNum + } + + //禁用设备数量 + disable, _ := cache.Instance().Get(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceDisable) + if disable.Val() != nil { + res.Disable = disable.Int() + } else { + m := dao.DevDevice.Ctx(ctx) + disable, _ := m.Where(dao.DevDevice.Columns().Status, model.DeviceStatusNoEnable).Count() + _ = cache.Instance().Set(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceDisable, disable, 0) + res.Disable = disable + } + res.Online = dcache.CountDeviceOnlineNum() + res.Offline = res.Total - res.Online + return +} + +// GetDeviceDataCountList 按年度每月设备消息统计,dataType 为统计数据类型 year:按年度,统计每个月的,month:按月份,统计每天的。当前的年与月 +func (s *sAnalysisDevice) GetDeviceDataCountList(ctx context.Context, dateType string) (res []model.CountData, err error) { + //res = make(map[string]int64) + // 获取当前时间 + now := time.Now() + // 获取当前年份 + year := now.Year() + // 获取当前月份 + month := now.Month() + + switch dateType { + case "month": + // 获取当前月份的天数 + days := utils.CalcDaysFromYearMonth(year, int(month)) + for i := 1; i <= days; i++ { + // 将日期格式化为字符串 + dayTag := fmt.Sprintf("%d-%02d-%02d", year, month, i) + key := consts.AnalysisDeviceCountPrefix + consts.TodayMessageVolume + utils.GetTimeTagGroup() + dayTag + countData, _ := cache.Instance().Get(context.Background(), key) + var cd model.CountData + cd.Title = gconv.String(i) + cd.Value = countData.Int64() + res = append(res, cd) + } + case "year": + for i := 1; i <= 12; i++ { + timeTag := fmt.Sprintf("%d:%02d", year, i) + keys, err := dcache.SearchKey(consts.AnalysisDeviceCountPrefix + consts.TodayMessageVolume + timeTag) + if err != nil { + g.Log().Error(context.Background(), err.Error()) + continue + } + var number int64 = 0 + for _, v := range keys { + countData, _ := cache.Instance().Get(context.Background(), v) + number = number + countData.Int64() + } + var cd model.CountData + cd.Title = gconv.String(i) + cd.Value = number + res = append(res, cd) + } + + default: + g.Log().Error(context.Background(), dateType, "dateType参数错误") + return + + } + return + +} + +// RemoveDeviceStatusCountCache 清除设备状态统计缓存 +func RemoveDeviceStatusCountCache(ctx context.Context) { + _, err := cache.Instance().Remove(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceTotal) + if err != nil { + g.Log().Debug(ctx, "清除设备状态统计缓存失败", err) + return + } + _, err = cache.Instance().Remove(ctx, consts.AnalysisDeviceCountPrefix+consts.DeviceDisable) + if err != nil { + g.Log().Debug(ctx, "清除设备状态统计缓存失败", err) + return + } +} + +// GetDeviceDataCountByToday 获取今日设备数据计数 +func GetDeviceDataCountByToday() int64 { + lastDate := utils.GetCurrentDateString() + res, _ := cache.Instance().Get(context.Background(), consts.AnalysisDeviceCountPrefix+consts.TodayMessageVolume+utils.GetTimeTagGroup()+lastDate) + return res.Int64() +} + +func (s *sAnalysisDevice) getDeviceDataCountTotal(ctx context.Context, dateType string) (number int64) { + switch dateType { + case "month": + dataList, err := s.GetDeviceDataCountList(ctx, "month") + if err != nil { + g.Log().Debug(ctx, "获取设备数据计数失败", err) + return + } + for _, v := range dataList { + var data = new(model.CountData) + err := gconv.Scan(v, &data) + if err != nil { + continue + } + number = number + data.Value + } + + case "year": + dataList, err := s.GetDeviceDataCountList(ctx, "year") + if err != nil { + g.Log().Debug(ctx, "获取设备数据计数失败", err) + return + } + for _, v := range dataList { + var data = new(model.CountData) + err := gconv.Scan(v, &data) + if err != nil { + continue + } + number = number + data.Value + } + + default: + keys, err := dcache.SearchKey(consts.AnalysisDeviceCountPrefix + consts.TodayMessageVolume) + if err != nil { + g.Log().Error(context.Background(), err.Error()) + return + } + for _, v := range keys { + countData, _ := cache.Instance().Get(context.Background(), v) + number = number + countData.Int64() + } + } + return +} diff --git a/internal/logic/analysis/deviceData.go b/internal/logic/analysis/deviceData.go new file mode 100644 index 0000000..99e48a0 --- /dev/null +++ b/internal/logic/analysis/deviceData.go @@ -0,0 +1,79 @@ +package analysis + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/dcache" + "sagooiot/pkg/general" +) + +type sAnalysisDeviceData struct { +} + +func init() { + service.RegisterAnalysisDeviceData(analysisDeviceDataNew()) +} + +func analysisDeviceDataNew() *sAnalysisDeviceData { + return &sAnalysisDeviceData{} +} + +// GetDeviceData 获取设备数据 +func (s *sAnalysisDeviceData) GetDeviceData(ctx context.Context, reqData model.DeviceDataReq) (res []interface{}, err error) { + deviceLogList := new(model.DeviceLogSearchOutput) + result, total, currentPage, err := dcache.GetDataByPage(ctx, reqData.DeviceKey, reqData.PageNum, reqData.PageSize, nil, reqData.DateRange) + if err != nil { + return + } + + deviceLogList.Total = total + deviceLogList.CurrentPage = currentPage + var logs []model.TdLog + if err := gconv.Scan(result, &logs); err != nil { + return nil, err + } + deviceLogList.List = logs + + // 基于物模型解析数据 + for _, log := range logs { + tmp, err := service.DevTSLParse().ParseData(ctx, reqData.DeviceKey, []byte(log.Content)) + if err != nil { + continue + } + res = append(res, tmp) + } + + return +} + +// GetDeviceDataForProductByLatest 获取产品下的所有设备最新一条数据 +func (s *sAnalysisDeviceData) GetDeviceDataForProductByLatest(ctx context.Context, productKey string) (res []model.DeviceDataRes, err error) { + deviceKeys := getDeviceKeys(ctx, productKey) + for _, key := range deviceKeys { + var deviceData model.DeviceDataRes + data := dcache.GetDeviceDetailDataByLatest(ctx, key) + if data != nil { + deviceData.DeviceKey = key + deviceData.DeviceData = data + res = append(res, deviceData) + } + } + return +} + +// GetDeviceHistoryData 获取设备历史数据(来自TSD的数据) +func (s *sAnalysisDeviceData) GetDeviceHistoryData(ctx context.Context, reqData model.DeviceDataReq) (res []interface{}, err error) { + + return +} + +// GetDeviceAlarmLogData 获取设备告警数据 +func (s *sAnalysisDeviceData) GetDeviceAlarmLogData(ctx context.Context, reqData *general.SelectReq) (res interface{}, err error) { + m := dao.AlarmLog.Ctx(ctx) + data, err := general.ListByPage(ctx, m, reqData, []string{"device_key", "rule_name", "product_key"}) + res = data + return +} diff --git a/internal/logic/analysis/deviceDataTsd.go b/internal/logic/analysis/deviceDataTsd.go new file mode 100644 index 0000000..4c09eab --- /dev/null +++ b/internal/logic/analysis/deviceDataTsd.go @@ -0,0 +1,82 @@ +package analysis + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "sagooiot/pkg/general" + "sagooiot/pkg/tsd" + "time" +) + +type sAnalysisDeviceDataTsd struct { +} + +func init() { + service.RegisterAnalysisDeviceDataTsd(analysisDeviceDataTsdNew()) +} + +func analysisDeviceDataTsdNew() *sAnalysisDeviceDataTsd { + return &sAnalysisDeviceDataTsd{} +} + +func (s *sAnalysisDeviceDataTsd) GetDeviceData(ctx context.Context, reqData general.SelectReq) (rs []interface{}, err error) { + // 创建数据库连接 + db := tsd.GetDB() + defer db.Close() + + if reqData.Param["deviceKey"] == nil { + err = fmt.Errorf("deviceKey is nil") + return + } + + sqlStr := fmt.Sprintf("select * from device_%s", reqData.Param["deviceKey"]) + rows, err := db.Query(sqlStr) + if err != nil { + return + } + defer rows.Close() + + columns, _ := rows.Columns() + + for rows.Next() { + values := make([]any, len(columns)) + for i := range values { + values[i] = new(any) + } + + err = rows.Scan(values...) + if err != nil { + return + } + + m := make(gdb.Record, len(columns)) + for i, c := range columns { + // 去除前缀 + if c[:2] == consts.TdPropertyPrefix { + c = c[2:] + } + m[c] = toTime(gvar.New(values[i])) + } + rs = append(rs, m) + } + return +} + +// Time REST连接时区处理 +func toTime(v *g.Var) (rs *g.Var) { + driver := g.Cfg().MustGet(context.TODO(), "tdengine.type") + if driver.String() == "taosRestful" { + if t, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", v.String()); err == nil { + rs = gvar.New(t.Local().Format("2006-01-02 15:04:05")) + return + } + } + + rs = v + return +} diff --git a/internal/logic/analysis/deviceDataTsd_test.go b/internal/logic/analysis/deviceDataTsd_test.go new file mode 100644 index 0000000..d7a1933 --- /dev/null +++ b/internal/logic/analysis/deviceDataTsd_test.go @@ -0,0 +1,23 @@ +package analysis + +import ( + "context" + _ "github.com/taosdata/driver-go/v3/taosRestful" + _ "github.com/taosdata/driver-go/v3/taosWS" + "sagooiot/internal/service" + "sagooiot/pkg/general" + + "testing" +) + +func TestGetDeviceDataByTsd(t *testing.T) { + var reqData general.SelectReq + reqData.Param = make(map[string]interface{}) + reqData.Param["deviceKey"] = "t202200001" + gotNumber, err := service.AnalysisDeviceDataTsd().GetDeviceData(context.Background(), reqData) + if err != nil { + t.Log(err) + } + t.Log(gotNumber) + +} diff --git a/internal/logic/analysis/deviceData_test.go b/internal/logic/analysis/deviceData_test.go new file mode 100644 index 0000000..4879b03 --- /dev/null +++ b/internal/logic/analysis/deviceData_test.go @@ -0,0 +1,37 @@ +package analysis + +import ( + "context" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/general" + "testing" +) + +// 测试获取设备在线离线统计数据 +func TestGetDeviceData(t *testing.T) { + // 设置缓存适配器 + cache.SetAdapter(context.Background()) + + var reqData model.DeviceDataReq + reqData.DeviceKey = "t202200002" + reqData.PageSize = 10 + reqData.PageNum = 1 + + data, err := service.AnalysisDeviceData().GetDeviceData(context.Background(), reqData) + if err != nil { + t.Log(err) + } + t.Log(data) +} + +func TestGetDeviceAlarmData(t *testing.T) { + var reqData = new(general.SelectReq) + reqData.KeyWords = "t202200002" + data, err := service.AnalysisDeviceData().GetDeviceAlarmLogData(context.Background(), reqData) + if err != nil { + t.Log(err) + } + t.Log(data) +} diff --git a/internal/logic/analysis/device_test.go b/internal/logic/analysis/device_test.go new file mode 100644 index 0000000..879e2aa --- /dev/null +++ b/internal/logic/analysis/device_test.go @@ -0,0 +1,65 @@ +package analysis + +import ( + "context" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "testing" + + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" +) + +// 测试获取设备在线离线统计数据 +func TestGetDeviceOnlineOfflineStats(t *testing.T) { + // 设置缓存适配器 + cache.SetAdapter(context.Background()) + data, err := service.AnalysisDevice().GetDeviceOnlineOfflineCount(context.Background()) + if err != nil { + t.Log(err) + } + t.Log(data) +} + +// 统计 本年度,年,月,日总数 +func TestGetDeviceDataNumberTotalStats(t *testing.T) { + cache.SetAdapter(context.Background()) + number, err := service.AnalysisDevice().GetDeviceDataTotalCount(context.Background(), "year") + if err != nil { + t.Log(err) + } + t.Log("今年总数:", number) + + number, err = service.AnalysisDevice().GetDeviceDataTotalCount(context.Background(), "month") + if err != nil { + t.Log(err) + } + t.Log("当月总数:", number) + + number, err = service.AnalysisDevice().GetDeviceDataTotalCount(context.Background(), "day") + if err != nil { + t.Log(err) + } + t.Log("今日总数:", number) + +} + +// 测试获取设备数据统计数据 +func TestGetCountDeviceDataNumberList(t *testing.T) { + // 设置缓存适配器 + cache.SetAdapter(context.Background()) + + // 按年统计,月度数据 + data, err := service.AnalysisDevice().GetDeviceDataCountList(context.Background(), "year") + if err != nil { + t.Log(err) + } + t.Log(data) + + //按月统计,日度数据,为当月数据 + data, err = service.AnalysisDevice().GetDeviceDataCountList(context.Background(), "month") + if err != nil { + t.Log(err) + } + t.Log(data) + +} diff --git a/internal/logic/analysis/product.go b/internal/logic/analysis/product.go new file mode 100644 index 0000000..bd7702a --- /dev/null +++ b/internal/logic/analysis/product.go @@ -0,0 +1,77 @@ +package analysis + +import ( + "context" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "time" +) + +type sAnalysisProduct struct { +} + +func init() { + service.RegisterAnalysisProduct(analysisProductNew()) +} + +func analysisProductNew() *sAnalysisProduct { + return &sAnalysisProduct{} +} + +// GetDeviceCountForProduct 获取产品下的设备数量 +func (s *sAnalysisProduct) GetDeviceCountForProduct(ctx context.Context, productKey string) (number int, err error) { + key := consts.AnalysisProductCountPrefix + consts.ProductDeviceCount + gconv.String(productKey) + resData, err := cache.Instance().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { + m := dao.DevDevice.Ctx(ctx) + value, err = m.Where(dao.DevDevice.Columns().ProductKey, productKey).Count() + if err != nil { + return + } + return + }, time.Hour*1) + if err != nil { + return + } + number = resData.Int() + return +} + +// GetProductCount 获取产品数量统计 +func (s *sAnalysisProduct) GetProductCount(ctx context.Context) (res model.ProductCountRes, err error) { + key := consts.AnalysisProductCountPrefix + "total" + resData, err := cache.Instance().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { + value, err = s.getTotalData(ctx) + return + }, time.Minute*1) + err = gconv.Struct(resData.Val(), &res) + return +} + +// getTotalData 从数据库中获取统计数据 +func (s *sAnalysisProduct) getTotalData(ctx context.Context) (data model.ProductCountRes, err error) { + m := dao.DevProduct.Ctx(ctx) + // 产品总量 + data.Total, err = m.Count() + if err != nil { + return + } + // 产品新增数量 + data.Added, err = m. + Where(dao.DevProduct.Columns().CreatedAt+">=?", gtime.Now().Format("Y-m-d")). + Count() + if err != nil { + return + } + //产品启用 + data.Enable, err = m.Where(dao.DevProduct.Columns().Status, 1).Count() + if err != nil { + return + } + data.Disable = data.Total - data.Enable + return +} diff --git a/internal/logic/analysis/product_test.go b/internal/logic/analysis/product_test.go new file mode 100644 index 0000000..5a93967 --- /dev/null +++ b/internal/logic/analysis/product_test.go @@ -0,0 +1,19 @@ +package analysis + +import ( + "context" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "testing" +) + +// 测试获取产品下设备数 +func TestGetDeviceCountForProduct(t *testing.T) { + // 设置缓存适配器 + cache.SetAdapter(context.Background()) + data, err := service.AnalysisProduct().GetDeviceCountForProduct(context.Background(), "monipower20221103") + if err != nil { + t.Log(err) + } + t.Log(data) +} diff --git a/internal/logic/common/base_db_link.go b/internal/logic/common/base_db_link.go deleted file mode 100644 index 5a8dac1..0000000 --- a/internal/logic/common/base_db_link.go +++ /dev/null @@ -1,170 +0,0 @@ -package common - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/gconv" -) - -type sBaseDbLink struct { -} - -func BaseDbLink() *sBaseDbLink { - return &sBaseDbLink{} -} - -func init() { - service.RegisterBaseDbLink(BaseDbLink()) -} - -// GetList 获取数据源数据列表 -func (s *sBaseDbLink) GetList(ctx context.Context, input *model.BaseDbLinkDoInput) (total int, out []*model.BaseDbLinkOut, err error) { - m := dao.BaseDbLink.Ctx(ctx) - if input.Host != "" { - m = m.WhereLike(dao.BaseDbLink.Columns().Host, "%"+input.Host+"%") - } - if input.Name != "" { - m = m.WhereLike(dao.BaseDbLink.Columns().Name, "%"+input.Name+"%") - } - if input.Types != "" { - m = m.WhereLike(dao.BaseDbLink.Columns().Types, "%"+input.Types+"%") - } - if input.Port != "" { - m = m.WhereLike(dao.BaseDbLink.Columns().Port, "%"+input.Port+"%") - } - if input.UserName != "" { - m = m.WhereLike(dao.BaseDbLink.Columns().UserName, "%"+input.UserName+"%") - } - if input.Status != -1 { - m = m.Where(dao.BaseDbLink.Columns().Status, input.Status) - } - m = m.Where(dao.BaseDbLink.Columns().IsDeleted, 0) - //获取总数 - total, err = m.Count() - if err != nil { - err = gerror.New("获取数据源列表数据失败") - return - } - if input.PageNum == 0 { - input.PageNum = 1 - } - if input.PageSize == 0 { - input.PageSize = consts.DefaultPageSize - } - //获取数据源列表信息 - err = m.Page(input.PageNum, input.PageSize).OrderDesc(dao.BaseDbLink.Columns().CreatedAt).Scan(&out) - if err != nil { - err = gerror.New("获取数据源列表失败") - return - } - return -} - -// Add 添加数据源 -func (s *sBaseDbLink) Add(ctx context.Context, input *model.AddBaseDbLinkInput) (err error) { - var baseDbLink *entity.BaseDbLink - //根据名称查看角色是否存在 - baseDbLink = checkBaseDbLinkName(ctx, input.Name, baseDbLink, 0) - if baseDbLink != nil { - return gerror.New("数据源已存在,无法添加") - } - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - baseDbLink = new(entity.BaseDbLink) - if err := gconv.Scan(input, &baseDbLink); err != nil { - return err - } - baseDbLink.IsDeleted = 0 - baseDbLink.CreatedBy = uint(loginUserId) - _, err = dao.BaseDbLink.Ctx(ctx).Data(baseDbLink).Insert() - if err != nil { - return err - } - return -} - -// Detail 数据源详情 -func (s *sBaseDbLink) Detail(ctx context.Context, baseDbLinkId int) (entity *entity.BaseDbLink, err error) { - _ = dao.BaseDbLink.Ctx(ctx).Where(g.Map{ - dao.BaseDbLink.Columns().Id: baseDbLinkId, - }).Scan(&entity) - if entity == nil { - return nil, gerror.New("ID错误") - } - return -} - -// Edit 修改数据源 -func (s *sBaseDbLink) Edit(ctx context.Context, input *model.EditBaseDbLinkInput) (err error) { - var baseDbLink, BaseDbLink2 *entity.BaseDbLink - //根据ID查看数据源是否存在 - baseDbLink = checkBaseDbLinkId(ctx, input.Id, baseDbLink) - if baseDbLink == nil { - return gerror.New("数据源不存在") - } - BaseDbLink2 = checkBaseDbLinkName(ctx, input.Name, BaseDbLink2, input.Id) - if BaseDbLink2 != nil { - return gerror.New("相同数据源已存在,无法修改") - } - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - if err := gconv.Scan(input, &baseDbLink); err != nil { - return err - } - baseDbLink.UpdatedBy = int(uint(loginUserId)) - _, err = dao.BaseDbLink.Ctx(ctx).Data(baseDbLink). - Where(dao.BaseDbLink.Columns().Id, input.Id).Update() - if err != nil { - return gerror.New("修改失败") - } - return -} - -// 检查相同数据源名称的数据是否存在 -func checkBaseDbLinkName(ctx context.Context, BaseDbLinkName string, BaseDbLink *entity.BaseDbLink, tag int) *entity.BaseDbLink { - m := dao.BaseDbLink.Ctx(ctx) - if tag > 0 { - m = m.WhereNot(dao.BaseDbLink.Columns().Id, tag) - } - _ = m.Where(g.Map{ - dao.BaseDbLink.Columns().Name: BaseDbLinkName, - dao.BaseDbLink.Columns().IsDeleted: 0, - }).Scan(&BaseDbLink) - return BaseDbLink -} - -// 检查指定ID的数据是否存在 -func checkBaseDbLinkId(ctx context.Context, BaseDbLinkId int, BaseDbLink *entity.BaseDbLink) *entity.BaseDbLink { - _ = dao.BaseDbLink.Ctx(ctx).Where(g.Map{ - dao.BaseDbLink.Columns().Id: BaseDbLinkId, - dao.BaseDbLink.Columns().IsDeleted: 0, - }).Scan(&BaseDbLink) - return BaseDbLink -} - -// Del 根据ID删除数据源信息 -func (s *sBaseDbLink) Del(ctx context.Context, BaseDbLinkId int) (err error) { - var BaseDbLink *entity.BaseDbLink - _ = dao.BaseDbLink.Ctx(ctx).Where(g.Map{ - dao.BaseDbLink.Columns().Id: BaseDbLinkId, - }).Scan(&BaseDbLink) - if BaseDbLink == nil { - return gerror.New("ID错误") - } - loginUserId := service.Context().GetUserId(ctx) - //删除数据源信息 - _, err = dao.BaseDbLink.Ctx(ctx). - Data(g.Map{ - dao.BaseDbLink.Columns().DeletedBy: uint(loginUserId), - dao.BaseDbLink.Columns().IsDeleted: 1, - }).Where(dao.BaseDbLink.Columns().Id, BaseDbLinkId). - Delete() - return -} diff --git a/internal/logic/common/cache.go b/internal/logic/common/cache.go deleted file mode 100644 index e349c19..0000000 --- a/internal/logic/common/cache.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -* @desc:缓存处理 - */ - -package common - -import ( - "github.com/sagoo-cloud/sagooiot/internal/consts" - "sync" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gctx" - "github.com/tiger1103/gfast-cache/cache" -) - -type ICache interface { - cache.IGCache -} - -type cacheImpl struct { - *cache.GfCache - prefix string -} - -var ( - c = cacheImpl{} - cacheContainer *cache.GfCache - lock = &sync.Mutex{} -) - -func Cache() ICache { - var ( - ch = c - ctx = gctx.New() - ) - prefix := g.Cfg().MustGet(ctx, "system.cache.prefix").String() - model := g.Cfg().MustGet(ctx, "system.cache.model").String() - if cacheContainer == nil { - lock.Lock() - if cacheContainer == nil { - if model == consts.CacheModelRedis { - // redis - cacheContainer = cache.NewRedis(prefix) - } else { - // memory - cacheContainer = cache.New(prefix) - } - } - lock.Unlock() - } - ch.GfCache = cacheContainer - return &ch -} diff --git a/internal/logic/common/check_auth.go b/internal/logic/common/check_auth.go new file mode 100644 index 0000000..51b37b7 --- /dev/null +++ b/internal/logic/common/check_auth.go @@ -0,0 +1,155 @@ +package common + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "strings" +) + +type sCheckAuth struct { +} + +func CheckAuth() *sCheckAuth { + return &sCheckAuth{} +} + +func init() { + service.RegisterCheckAuth(CheckAuth()) +} + +// IsToken 验证TOKEN是否正确 +func (s *sCheckAuth) IsToken(ctx context.Context) (isToken bool, expiresAt int64, isAuth string, err error) { + authorization := ghttp.RequestFromCtx(ctx).Header.Get("Authorization") + if authorization == "" { + err = gerror.New("请先登录!") + return + } + isToken = false + expiresAt = 0 + //验证TOKEN是否正确 + data, _ := service.SysToken().ParseToken(ghttp.RequestFromCtx(ctx)) + if data != nil { + isToken = true + expiresAt = gconv.Int64(data.ExpiresAt) + } + //获取当前登录用户账户 + userName := service.Context().GetUserName(ctx) + + dict, err := service.DictData().GetDictDataByType(ctx, "rule_engine_user_blacklist") + if err != nil { + return + } + isAuth = "save" + if dict != nil && dict.Values != nil && len(dict.Values) > 0 { + for _, v := range dict.Values { + if strings.EqualFold(v.DictValue, userName) { + isAuth = "read" + break + } + } + } + return +} + +// CheckAccessAuth 验证访问权限 +func (s *sCheckAuth) CheckAccessAuth(ctx context.Context, address string) (isAllow bool, err error) { + isAllow = false + //查询API开关是否打开 + sysApiSwitchConfig, _ := service.ConfigData().GetConfigByKey(ctx, "sys.api.switch") + sysApiSwitch := 0 + if sysApiSwitchConfig != nil { + sysApiSwitch = gconv.Int(sysApiSwitchConfig.ConfigValue) + } + if sysApiSwitch == 0 { + isAllow = true + return + } + + //获取用户角色信息 + userRoleInfo, err := service.SysUserRole().GetInfoByUserId(ctx, service.Context().GetUserId(ctx)) + if err != nil { + err = gerror.New("获取用户角色失败") + return + } + if userRoleInfo == nil { + err = gerror.New("用户未配置角色信息,请联系管理员") + return + } + + var roleIds []int + //判断是否为超级管理员 + var isSuperAdmin = false + for _, userRole := range userRoleInfo { + //获取角色ID + if userRole.RoleId == 1 { + isSuperAdmin = true + } + roleIds = append(roleIds, userRole.RoleId) + } + + //超级管理员拥有所有访问权限 + if isSuperAdmin { + isAllow = true + return + } + + //获取角色ID下所有的请求API + authorizeInfo, authorizeErr := service.SysAuthorize().GetInfoByRoleIdsAndItemsType(ctx, roleIds, consts.Api) + if authorizeErr != nil { + err = gerror.New("获取用户权限失败") + return + } + + if authorizeInfo == nil || len(authorizeInfo) == 0 { + err = gerror.New("未授权接口,无访问权限!") + return + } + + //判断是否与当前访问接口一致 + var menuApiIds []int + for _, authorize := range authorizeInfo { + menuApiIds = append(menuApiIds, authorize.ItemsId) + } + //获取所有的接口API + menuApiInfo, menuApiErr := service.SysMenuApi().GetInfoByIds(ctx, menuApiIds) + if menuApiErr != nil { + err = gerror.New("相关接口未配置") + return + } + if menuApiInfo == nil || len(menuApiInfo) == 0 { + err = gerror.New("接口未绑定菜单,请联系管理员!") + return + } + var apiIds []int + for _, menuApi := range menuApiInfo { + apiIds = append(apiIds, menuApi.ApiId) + } + //获取所有的接口 + apiInfo, apiErr := service.SysApi().GetInfoByIds(ctx, apiIds) + if apiErr != nil { + err = gerror.New("获取接口失败") + return + } + if apiInfo == nil || len(apiInfo) == 0 { + err = gerror.New("相关接口未配置") + return + } + + var isExist = false + //获取请求路径 + for _, api := range apiInfo { + if strings.EqualFold(address, api.Address) { + isExist = true + break + } + } + if isExist { + isAllow = true + return + } + return +} diff --git a/internal/logic/common/city_data.go b/internal/logic/common/city_data.go deleted file mode 100644 index 1f90ceb..0000000 --- a/internal/logic/common/city_data.go +++ /dev/null @@ -1,178 +0,0 @@ -package common - -import ( - "context" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" -) - -type sCityData struct { -} - -func CityData() *sCityData { - return &sCityData{} -} - -func init() { - service.RegisterCityData(CityData()) -} - -// GetList 获取城市列表 -func (s *sCityData) GetList(ctx context.Context, status int, name string, code string) (data []*entity.CityData, err error) { - m := dao.CityData.Ctx(ctx) - if status != -1 { - m = m.Where(dao.CityData.Columns().Status, status) - } - if name != "" { - m = m.Where(dao.CityData.Columns().Name, name) - } - if code != "" { - m = m.Where(dao.CityData.Columns().Code, code) - } - m = m.Where(dao.CityData.Columns().IsDeleted, 0).OrderAsc(dao.CityData.Columns().Sort) - - //获取城市列表信息 - err = m.Scan(&data) - if err != nil { - err = gerror.New("获取城市列表失败") - return - } - return -} - -// Add 添加城市 -func (s *sCityData) Add(ctx context.Context, city *entity.CityData) (err error) { - err = dao.CityData.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - //根据名字查询城市是否存在 - num, _ := dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Name: city.Name, - dao.CityData.Columns().IsDeleted: 0, - }).Count() - if num > 0 { - return gerror.New("城市已存在") - } - //根据Code查询城市是否存在 - codeNum, _ := dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Code: city.Code, - dao.CityData.Columns().IsDeleted: 0, - }).Count() - if codeNum > 0 { - return gerror.New("编码已存在") - } - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - city.CreatedBy = uint(loginUserId) - city.IsDeleted = 0 - _, addErr := dao.CityData.Ctx(ctx).Data(city).Insert() - if addErr != nil { - err = gerror.New("添加失败") - return err - } - return err - }) - - return -} - -// Edit 编辑城市 -func (s *sCityData) Edit(ctx context.Context, city *entity.CityData) (err error) { - //根据ID查询城市是否存在 - var cityInfo *entity.CityData - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Id: city.Id, - dao.CityData.Columns().IsDeleted: 0, - }).Scan(&cityInfo) - if cityInfo == nil { - return gerror.New("ID错误") - } - var cityInfoByName *entity.CityData - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Name: city.Name, - dao.CityData.Columns().IsDeleted: 0, - }).Scan(&cityInfoByName) - if cityInfoByName != nil && cityInfoByName.Id != city.Id { - return gerror.New("编码已存在") - } - var cityInfoByCode *entity.CityData - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Code: city.Code, - dao.CityData.Columns().IsDeleted: 0, - }).Scan(&cityInfoByCode) - if cityInfoByCode != nil && cityInfoByCode.Id != city.Id { - return gerror.New("编码已存在") - } - - err = dao.CityData.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - cityInfo.UpdatedBy = uint(loginUserId) - cityInfo.Status = city.Status - cityInfo.Sort = city.Sort - cityInfo.ParentId = city.ParentId - cityInfo.Name = city.Name - cityInfo.Code = city.Code - _, err = dao.CityData.Ctx(ctx).Data(cityInfo).Where(dao.CityData.Columns().Id, city.Id).Update() - if err != nil { - return gerror.New("修改失败") - } - return err - }) - - return -} - -// GetInfoById 根据ID获取城市 -func (s *sCityData) GetInfoById(ctx context.Context, id int) (cityInfo *entity.CityData, err error) { - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Id: id, - }).Scan(&cityInfo) - return -} - -// DelById 删除城市 -func (s *sCityData) DelById(ctx context.Context, id int) (err error) { - //根据ID查询城市是否存在 - var cityInfo *entity.CityData - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Id: id, - dao.CityData.Columns().IsDeleted: 0, - }).Scan(&cityInfo) - if cityInfo == nil { - return gerror.New("ID错误") - } - //判断是否存在子节点 - childrenNum, err := dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().ParentId: id, - dao.CityData.Columns().IsDeleted: 0, - }).Count() - if childrenNum > 0 { - return gerror.New("请先删除子节点") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - cityInfo.DeletedBy = loginUserId - cityInfo.IsDeleted = 1 - //获取当前时间 - t, err := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") - cityInfo.DeletedAt = t - _, err = dao.CityData.Ctx(ctx).Data(cityInfo).Where(dao.CityData.Columns().Id, id).Update() - if err != nil { - return gerror.New("删除失败") - } - return -} - -// GetAll 获取所有城市 -func (s *sCityData) GetAll(ctx context.Context) (data []*entity.CityData, err error) { - err = dao.CityData.Ctx(ctx).Where(g.Map{ - dao.CityData.Columns().Status: 1, - dao.CityData.Columns().IsDeleted: 0, - }).OrderAsc(dao.CityData.Columns().Sort).Scan(&data) - return -} diff --git a/internal/logic/common/config_data.go b/internal/logic/common/config_data.go index d7860bd..4acac22 100644 --- a/internal/logic/common/config_data.go +++ b/internal/logic/common/config_data.go @@ -3,17 +3,18 @@ package common import ( "context" "errors" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" - "time" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sConfigData struct { @@ -40,15 +41,17 @@ func (s *sConfigData) List(ctx context.Context, input *model.ConfigDoInput) (tot if input.ConfigKey != "" { m = m.WhereLike(dao.SysConfig.Columns().ConfigKey, "%"+input.ConfigKey+"%") } - if input.ModuleClassify != "" { - m = m.Where(dao.SysConfig.Columns().ModuleClassify, input.ModuleClassify) - } if len(input.DateRange) > 0 { m = m.WhereBetween(dao.SysConfig.Columns().CreatedAt, input.DateRange[0], input.DateRange[1]) } + if input.ModuleClassify != "" { + m = m.Where(dao.SysConfig.Columns().ModuleClassify, input.ModuleClassify) + } } total, err = m.Count() - liberr.ErrIsNil(ctx, err, "获取数据失败") + if err != nil { + return 0, nil, errors.New("获取数据失败") + } if input.PageNum == 0 { input.PageNum = 1 } @@ -56,85 +59,109 @@ func (s *sConfigData) List(ctx context.Context, input *model.ConfigDoInput) (tot input.PageSize = consts.PageSize } err = m.Page(input.PageNum, input.PageSize).Order("config_id desc").Scan(&out) - liberr.ErrIsNil(ctx, err, "获取数据失败") + if err != nil { + return 0, nil, errors.New("获取数据失败") + } return } func (s *sConfigData) Add(ctx context.Context, input *model.AddConfigInput, userId int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = s.CheckConfigKeyUnique(ctx, input.ConfigKey) - liberr.ErrIsNil(ctx, err) - _, err = dao.SysConfig.Ctx(ctx).Insert(do.SysConfig{ - ConfigName: input.ConfigName, - ConfigKey: input.ConfigKey, - ConfigValue: input.ConfigValue, - ConfigType: input.ConfigType, - ModuleClassify: input.ModuleClassify, - CreateBy: userId, - Remark: input.Remark, - }) - liberr.ErrIsNil(ctx, err, "添加系统参数失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysConfigTag) - }) + err = s.CheckConfigKeyUnique(ctx, input.ConfigKey) + if err != nil { + return + } + data := &do.SysConfig{ + ConfigName: input.ConfigName, + ConfigKey: input.ConfigKey, + ConfigValue: input.ConfigValue, + ConfigType: input.ConfigType, + CreatedBy: userId, + Remark: input.Remark, + ModuleClassify: input.ModuleClassify, + Status: 1, + IsDeleted: 0, + } + _, err = dao.SysConfig.Ctx(ctx).Insert(data) + if err != nil { + return errors.New("添加系统参数失败") + } + + //添加到缓存 + err = cache.Instance().Set(ctx, consts.SystemConfigPrefix+input.ConfigKey, data, 0) + if err != nil { + return + } + return } // CheckConfigKeyUnique 验证参数键名是否存在 func (s *sConfigData) CheckConfigKeyUnique(ctx context.Context, configKey string, configId ...int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - data := (*entity.SysConfig)(nil) - m := dao.SysConfig.Ctx(ctx).Fields(dao.SysConfig.Columns().ConfigId).Where(dao.SysConfig.Columns().ConfigKey, configKey).Unscoped() - if len(configId) > 0 { - m = m.WhereNot(dao.SysConfig.Columns().ConfigId, configId[0]) - } - err = m.Scan(&data) - liberr.ErrIsNil(ctx, err, "校验失败") - if data != nil { - liberr.ErrIsNil(ctx, errors.New("参数键名重复")) - } - }) + data := (*entity.SysConfig)(nil) + m := dao.SysConfig.Ctx(ctx).Fields(dao.SysConfig.Columns().ConfigId).Where(dao.SysConfig.Columns().ConfigKey, configKey) + if len(configId) > 0 { + m = m.Where(dao.SysConfig.Columns().ConfigId+" != ?", configId[0]) + } + err = m.Scan(&data) + if err != nil { + return + } + if data != nil { + return errors.New("参数键名重复") + } + return } // Get 获取系统参数 func (s *sConfigData) Get(ctx context.Context, id int) (out *model.SysConfigOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = dao.SysConfig.Ctx(ctx).WherePri(id).Scan(&out) - liberr.ErrIsNil(ctx, err, "获取系统参数失败") - }) + err = dao.SysConfig.Ctx(ctx).WherePri(id).Scan(&out) + if err != nil { + return nil, errors.New("获取系统参数失败") + } return } // Edit 修改系统参数 func (s *sConfigData) Edit(ctx context.Context, input *model.EditConfigInput, userId int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = s.CheckConfigKeyUnique(ctx, input.ConfigKey, input.ConfigId) - liberr.ErrIsNil(ctx, err) - _, err = dao.SysConfig.Ctx(ctx).WherePri(input.ConfigId).Update(do.SysConfig{ - ConfigName: input.ConfigName, - ConfigKey: input.ConfigKey, - ConfigValue: input.ConfigValue, - ConfigType: input.ConfigType, - ModuleClassify: input.ModuleClassify, - UpdateBy: userId, - Remark: input.Remark, - }) - liberr.ErrIsNil(ctx, err, "修改系统参数失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysConfigTag) - }) + err = s.CheckConfigKeyUnique(ctx, input.ConfigKey, input.ConfigId) + if err != nil { + return errors.New("参数键名重复") + } + data := &do.SysConfig{ + ConfigName: input.ConfigName, + ConfigKey: input.ConfigKey, + ConfigValue: input.ConfigValue, + ConfigType: input.ConfigType, + UpdatedBy: userId, + Remark: input.Remark, + ModuleClassify: input.ModuleClassify, + Status: 1, + IsDeleted: 0, + } + _, err = dao.SysConfig.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: -1, + Name: "ConfigDataByKey", + Force: false, + }).WherePri(input.ConfigId).Update(data) + if err != nil { + return errors.New("修改系统参数失败") + } + //更新缓存 + _, _, err = cache.Instance().Update(ctx, consts.SystemConfigPrefix+input.ConfigKey, data) + return } -// Delete 删除系统参数 +// Delete 删除系统参数 //TODO 转为KEY处理 func (s *sConfigData) Delete(ctx context.Context, ids []int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.SysConfig.Ctx(ctx).Delete(dao.SysConfig.Columns().ConfigId+" in (?)", ids) - liberr.ErrIsNil(ctx, err, "删除失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysConfigTag) - }) + _, err = dao.SysConfig.Ctx(ctx).Delete(dao.SysConfig.Columns().ConfigId+" in (?)", ids) + if err != nil { + return errors.New("删除失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.SystemConfigPrefix) + return } @@ -144,29 +171,126 @@ func (s *sConfigData) GetConfigByKey(ctx context.Context, key string) (config *e err = gerror.New("参数key不能为空") return } - cache := Cache() - cf := cache.Get(ctx, consts.CacheSysConfigTag+key) + cf, err := cache.Instance().Get(ctx, consts.SystemConfigPrefix+key) if cf != nil && !cf.IsEmpty() { - err = gconv.Struct(cf, &config) - return - } - config, err = s.GetByKey(ctx, key) - if err != nil { + err = gconv.Struct(cf.Val(), &config) return + } else { + config, err = s.GetByKey(ctx, key) + if err != nil { + return + } + if config != nil { + err = cache.Instance().Set(ctx, consts.SystemConfigPrefix+key, config, 0) + if err != nil { + return + } + } } - if config != nil { - //配置数据缓存1分钟 - cache.Set(ctx, consts.CacheSysConfigTag+key, config, time.Minute*1, consts.CacheSysConfigTag) + return +} + +// GetConfigByKeys 通过key数组获取参数(从缓存获取) +func (s *sConfigData) GetConfigByKeys(ctx context.Context, keys []string) (out []*entity.SysConfig, err error) { + + for _, key := range keys { + var config *entity.SysConfig + config, err = s.GetConfigByKey(ctx, key) + if err != nil { + return + } + out = append(out, config) } return } // GetByKey 通过key获取参数(从数据库获取) func (s *sConfigData) GetByKey(ctx context.Context, key string) (config *entity.SysConfig, err error) { - err = dao.SysConfig.Ctx(ctx).Where("config_key", key).Scan(&config) + err = dao.SysConfig.Ctx(ctx).Where(dao.SysConfig.Columns().ConfigKey, key).Scan(&config) if err != nil { g.Log().Error(ctx, err) err = gerror.New("获取配置失败") } return } + +// GetByKeys 通过keys获取参数(从数据库获取) +func (s *sConfigData) GetByKeys(ctx context.Context, keys []string) (config []*entity.SysConfig, err error) { + err = dao.SysConfig.Ctx(ctx).WhereIn(dao.SysConfig.Columns().ConfigKey, keys).Scan(&config) + if err != nil { + g.Log().Error(ctx, err) + err = gerror.New("获取配置失败") + } + return +} + +func (s *sConfigData) GetSysConfigSetting(ctx context.Context, types int) (out []*entity.SysConfig, err error) { + var keys []string + if types == 0 { + keys = []string{ + consts.SysSystemName, + consts.SysSystemCopyright, + consts.SysSystemLogo, + consts.SysSystemLoginPic, + consts.SysSystemLogoMini, + } + } else if types == 1 { + keys = []string{ + consts.SysColumnSwitch, + consts.SysButtonSwitch, + consts.SysApiSwitch, + consts.SysIsSingleLogin, + consts.SysTokenExpiryDate, + consts.SysPasswordChangePeriod, + consts.SysPasswordErrorNum, + consts.SysAgainLoginDate, + consts.SysPasswordMinimumLength, + consts.SysRequireComplexity, + consts.SysRequireDigit, + consts.SysRequireLowercaseLetter, + consts.SysRequireUppercaseLetter, + consts.SysIsSecurityControlEnabled, + consts.SysChangePasswordForFirstLogin, + consts.SysPasswordChangePeriodSwitch, + consts.SysIsRsaEnabled, + } + } + if len(keys) == 0 { + err = gerror.New("类型选择错误") + return + } + out, err = s.GetByKeys(ctx, keys) + if err != nil { + return + } + return +} + +// EditSysConfigSetting 修改系统配置设置 +func (s *sConfigData) EditSysConfigSetting(ctx context.Context, inputs []*model.EditConfigInput) (err error) { + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + for _, input := range inputs { + err = s.CheckConfigKeyUnique(ctx, input.ConfigKey, input.ConfigId) + if err != nil { + return errors.New("参数键名重复") + } + _, err = dao.SysConfig.Ctx(ctx).Where(dao.SysConfig.Columns().ConfigKey, input.ConfigKey).Update(do.SysConfig{ + ConfigValue: input.ConfigValue, + UpdatedBy: loginUserId, + UpdatedAt: gtime.Now(), + }) + if err != nil { + return errors.New("修改系统基础配置失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.SystemConfigPrefix+input.ConfigKey) + } + return +} + +// GetLoadCache 获取本地缓存配置 +func (s *sConfigData) GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error) { + err = g.Cfg().MustGet(ctx, "cache").Scan(&conf) + return +} diff --git a/internal/logic/common/dict_data.go b/internal/logic/common/dict_data.go index cb0435c..b6d41d3 100644 --- a/internal/logic/common/dict_data.go +++ b/internal/logic/common/dict_data.go @@ -2,15 +2,16 @@ package common import ( "context" + "errors" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sDictData struct { @@ -26,31 +27,29 @@ func init() { // GetDictWithDataByType 通过字典键类型获取选项 func (s *sDictData) GetDictWithDataByType(ctx context.Context, input *model.GetDictInput) (dict *model.GetDictOut, err error) { - cache := Cache() cacheKey := consts.CacheSysDict + "_" + input.DictType //从缓存获取 - iDict := cache.GetOrSetFuncLock(ctx, cacheKey, func(ctx context.Context) (value interface{}, err error) { - err = g.Try(ctx, func(ctx context.Context) { - //从数据库获取 - dict = &model.GetDictOut{} - //获取类型数据 - err = dao.SysDictType.Ctx(ctx).Where(g.Map{ - dao.SysDictType.Columns().DictType: input.DictType, - dao.SysDictType.Columns().Status: 1, - }).Fields(model.DictTypeOut{}).Scan(&dict.Data) - liberr.ErrIsNil(ctx, err, "获取字典类型失败") - err = dao.SysDictData.Ctx(ctx).Fields(model.DictDataOut{}). - Where(g.Map{ - dao.SysDictData.Columns().DictType: input.DictType, - }). - Order(dao.SysDictData.Columns().DictSort + " asc," + - dao.SysDictData.Columns().DictCode + " asc"). - Scan(&dict.Values) - liberr.ErrIsNil(ctx, err, "获取字典数据失败") - }) + iDict, err := cache.Instance().GetOrSetFuncLock(ctx, cacheKey, func(ctx context.Context) (value interface{}, err error) { + //从数据库获取 + dict = &model.GetDictOut{} + //获取类型数据 + err = dao.SysDictType.Ctx(ctx).Where(dao.SysDictType.Columns().DictType, input.DictType). + Where(dao.SysDictType.Columns().Status, 1).Fields(model.DictTypeOut{}).Scan(&dict.Data) + if err != nil { + return nil, errors.New("获取字典类型失败") + } + err = dao.SysDictData.Ctx(ctx).Fields(model.DictDataOut{}). + Where(dao.SysDictData.Columns().DictType, input.DictType). + Order(dao.SysDictData.Columns().DictSort + " asc," + + dao.SysDictData.Columns().DictCode + " asc"). + Scan(&dict.Values) + if err != nil { + return nil, errors.New("获取字典数据失败") + } + value = dict return - }, 0, consts.CacheSysDictTag) + }, 0) if iDict != nil { err = gconv.Struct(iDict, &dict) if err != nil { @@ -72,93 +71,134 @@ func (s *sDictData) GetDictWithDataByType(ctx context.Context, input *model.GetD // List 获取字典数据 func (s *sDictData) List(ctx context.Context, input *model.SysDictSearchInput) (total int, out []*model.SysDictDataOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.SysDictData.Ctx(ctx) - if input != nil { - if input.DictLabel != "" { - m = m.Where(dao.SysDictData.Columns().DictLabel+" like ?", "%"+input.DictLabel+"%") - } - if input.Status != "-1" { - m = m.Where(dao.SysDictData.Columns().Status+" = ", gconv.Int(input.Status)) - } - if input.DictType != "" { - m = m.Where(dao.SysDictData.Columns().DictType+" = ?", input.DictType) - } - total, err = m.Count() - liberr.ErrIsNil(ctx, err, "获取字典数据失败") - if input.PageNum == 0 { - input.PageNum = 1 - } + m := dao.SysDictData.Ctx(ctx) + if input != nil { + if input.DictLabel != "" { + m = m.Where(dao.SysDictData.Columns().DictLabel+" like ?", "%"+input.DictLabel+"%") } - if input.PageSize == 0 { - input.PageSize = consts.PageSize + if input.Status != "-1" { + m = m.Where(dao.SysDictData.Columns().Status+" = ", gconv.Int(input.Status)) } - err = m.Page(input.PageNum, input.PageSize).Order(dao.SysDictData.Columns().DictSort + " asc," + - dao.SysDictData.Columns().DictCode + " asc").Scan(&out) - liberr.ErrIsNil(ctx, err, "获取字典数据失败") - }) + if input.DictType != "" { + m = m.Where(dao.SysDictData.Columns().DictType+" = ?", input.DictType) + } + total, err = m.Count() + if err != nil { + return 0, nil, errors.New("获取字典数据失败") + } + if input.PageNum == 0 { + input.PageNum = 1 + } + } + if input.PageSize == 0 { + input.PageSize = consts.PageSize + } + err = m.Page(input.PageNum, input.PageSize).Order(dao.SysDictData.Columns().DictSort + " asc," + + dao.SysDictData.Columns().DictCode + " asc").Scan(&out) + if err != nil { + return 0, nil, errors.New("获取字典数据失败") + } return } func (s *sDictData) Add(ctx context.Context, input *model.AddDictDataInput, userId int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.SysDictData.Ctx(ctx).Insert(do.SysDictData{ - DictSort: input.DictSort, - DictLabel: input.DictLabel, - DictValue: input.DictValue, - DictType: input.DictType, - CssClass: input.CssClass, - ListClass: input.ListClass, - IsDefault: input.IsDefault, - Status: input.Status, - CreateBy: userId, - Remark: input.Remark, - }) - liberr.ErrIsNil(ctx, err, "添加字典数据失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) + _, err = dao.SysDictData.Ctx(ctx).Insert(do.SysDictData{ + DictSort: input.DictSort, + DictLabel: input.DictLabel, + DictValue: input.DictValue, + DictType: input.DictType, + CssClass: input.CssClass, + ListClass: input.ListClass, + IsDefault: input.IsDefault, + Status: input.Status, + CreatedBy: userId, + Remark: input.Remark, }) + if err != nil { + return errors.New("添加字典数据失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) + return } // Get 获取字典数据 func (s *sDictData) Get(ctx context.Context, dictCode uint) (out *model.SysDictDataOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = dao.SysDictData.Ctx(ctx).WherePri(dictCode).Scan(&out) - liberr.ErrIsNil(ctx, err, "获取字典数据失败") - }) + err = dao.SysDictData.Ctx(ctx).WherePri(dictCode).Scan(&out) + if err != nil { + return nil, errors.New("获取字典数据失败") + } + return } // Edit 修改字典数据 func (s *sDictData) Edit(ctx context.Context, input *model.EditDictDataInput, userId int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.SysDictData.Ctx(ctx).WherePri(input.DictCode).Update(do.SysDictData{ - DictSort: input.DictSort, - DictLabel: input.DictLabel, - DictValue: input.DictValue, - DictType: input.DictType, - CssClass: input.CssClass, - ListClass: input.ListClass, - IsDefault: input.IsDefault, - Status: input.Status, - UpdateBy: userId, - Remark: input.Remark, - }) - liberr.ErrIsNil(ctx, err, "修改字典数据失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) + _, err = dao.SysDictData.Ctx(ctx).WherePri(input.DictCode).Update(do.SysDictData{ + DictSort: input.DictSort, + DictLabel: input.DictLabel, + DictValue: input.DictValue, + DictType: input.DictType, + CssClass: input.CssClass, + ListClass: input.ListClass, + IsDefault: input.IsDefault, + Status: input.Status, + UpdatedBy: userId, + Remark: input.Remark, }) + if err != nil { + return errors.New("修改字典数据失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) + return } // Delete 删除字典数据 func (s *sDictData) Delete(ctx context.Context, ids []int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.SysDictData.Ctx(ctx).Where(dao.SysDictData.Columns().DictCode+" in(?)", ids).Delete() - liberr.ErrIsNil(ctx, err, "删除字典数据失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) - }) + _, err = dao.SysDictData.Ctx(ctx).Where(dao.SysDictData.Columns().DictCode+" in(?)", ids).Delete() + if err != nil { + return errors.New("删除字典数据失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) + + return +} + +// GetDictDataByType 通过字典键类型获取选项 +func (s *sDictData) GetDictDataByType(ctx context.Context, dictType string) (dict *model.GetDictOut, err error) { + cacheKey := consts.CacheSysDict + "_" + dictType + //从缓存获取 + iDict, err := cache.Instance().GetOrSetFuncLock(ctx, cacheKey, func(ctx context.Context) (value interface{}, err error) { + err = g.Try(ctx, func(ctx context.Context) { + //从数据库获取 + dict = &model.GetDictOut{} + //获取类型数据 + err = dao.SysDictType.Ctx(ctx).Where(dao.SysDictType.Columns().DictType, dictType). + Where(dao.SysDictType.Columns().Status, 1).Fields(model.DictTypeOut{}).Scan(&dict.Data) + if err != nil { + return + } + err = dao.SysDictData.Ctx(ctx).Fields(model.DictDataOut{}). + Where(dao.SysDictData.Columns().DictType, dictType). + Order(dao.SysDictData.Columns().DictSort + " asc," + + dao.SysDictData.Columns().DictCode + " asc"). + Scan(&dict.Values) + if err != nil { + return + } + }) + value = dict + return + }, 0) + if iDict != nil { + err = gconv.Struct(iDict, &dict) + if err != nil { + return + } + } return } diff --git a/internal/logic/common/dict_type.go b/internal/logic/common/dict_type.go index 5b18de0..cf833f7 100644 --- a/internal/logic/common/dict_type.go +++ b/internal/logic/common/dict_type.go @@ -2,19 +2,19 @@ package common import ( "context" + "errors" "github.com/gogf/gf/v2/container/garray" "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "sagooiot/api/v1/common" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sDictType struct { @@ -30,30 +30,32 @@ func init() { // List 字典类型列表 func (s *sDictType) List(ctx context.Context, input *model.DictTypeDoInput) (total int, out []*model.SysDictTypeInfoOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.SysDictType.Ctx(ctx) - if input.ModuleClassify != "" { - m = m.Where(dao.SysDictType.Columns().ModuleClassify, input.ModuleClassify) - } - if input.Status != "" { - m = m.Where(dao.SysDictType.Columns().Status, gconv.Int(input.Status)) - } - if input.DictName != "" { - m = m.WhereLike(dao.SysDictType.Columns().DictName, "%"+input.DictName+"%") - m = m.WhereOrLike(dao.SysDictType.Columns().DictType, "%"+input.DictName+"%") - } - total, err = m.Count() - liberr.ErrIsNil(ctx, err, "获取字典类型失败") - if input.PageNum == 0 { - input.PageNum = 1 - } - if input.PageSize == 0 { - input.PageSize = consts.PageSize - } - err = m.Fields(model.SysDictTypeInfoRes{}).Page(input.PageNum, input.PageSize). - Order(dao.SysDictType.Columns().DictId + " asc").Scan(&out) - liberr.ErrIsNil(ctx, err, "获取字典类型失败") - }) + m := dao.SysDictType.Ctx(ctx) + if input.ModuleClassify != "" { + m = m.Where(dao.SysDictType.Columns().ModuleClassify, input.ModuleClassify) + } + if input.Status != "" { + m = m.Where(dao.SysDictType.Columns().Status, gconv.Int(input.Status)) + } + if input.DictName != "" { + m = m.WhereLike(dao.SysDictType.Columns().DictName, "%"+input.DictName+"%") + m = m.WhereOrLike(dao.SysDictType.Columns().DictType, "%"+input.DictName+"%") + } + total, err = m.Count() + if err != nil { + return 0, nil, errors.New("获取字典类型失败") + } + if input.PageNum == 0 { + input.PageNum = 1 + } + if input.PageSize == 0 { + input.PageSize = consts.PageSize + } + err = m.Fields(model.SysDictTypeInfoRes{}).Page(input.PageNum, input.PageSize). + OrderDesc(dao.SysDictType.Columns().CreatedAt).Scan(&out) + if err != nil { + return 0, nil, errors.New("获取字典类型失败") + } return } @@ -61,102 +63,120 @@ func (s *sDictType) List(ctx context.Context, input *model.DictTypeDoInput) (tot func (s *sDictType) Add(ctx context.Context, input *model.AddDictTypeInput, userId int) (err error) { err = g.Try(ctx, func(ctx context.Context) { err = s.ExistsDictType(ctx, input.DictType) - liberr.ErrIsNil(ctx, err) - parentId := 0 + if err != nil { + return + } _, err = dao.SysDictType.Ctx(ctx).Insert(do.SysDictType{ DictName: input.DictName, DictType: input.DictType, Status: input.Status, - CreateBy: userId, + CreatedBy: userId, Remark: input.Remark, ModuleClassify: input.ModuleClassify, - ParentId: parentId, }) - liberr.ErrIsNil(ctx, err, "添加字典类型失败") + if err != nil { + return + } //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) }) return } // Edit 修改字典类型 func (s *sDictType) Edit(ctx context.Context, input *model.EditDictTypeInput, userId int) (err error) { - err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - err = g.Try(ctx, func(ctx context.Context) { - err = s.ExistsDictType(ctx, input.DictType, input.DictId) - liberr.ErrIsNil(ctx, err) - dictType := (*entity.SysDictType)(nil) - e := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictType).WherePri(input.DictId).Scan(&dictType) - liberr.ErrIsNil(ctx, e, "获取字典类型失败") - liberr.ValueIsNil(dictType, "字典类型不存在") - //修改字典类型 - _, e = dao.SysDictType.Ctx(ctx).TX(tx).WherePri(input.DictId).Update(do.SysDictType{ - DictName: input.DictName, - DictType: input.DictType, - Status: input.Status, - UpdateBy: userId, - Remark: input.Remark, - ModuleClassify: input.ModuleClassify, - }) - liberr.ErrIsNil(ctx, e, "修改字典类型失败") - //修改字典数据 - _, e = dao.SysDictData.Ctx(ctx).TX(tx).Data(do.SysDictData{DictType: input.DictType}). - Where(dao.SysDictData.Columns().DictType, dictType.DictType).Update() - liberr.ErrIsNil(ctx, e, "修改字典数据失败") - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) + err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + err = s.ExistsDictType(ctx, input.DictType, input.DictId) + if err != nil { + return nil + } + dictType := (*entity.SysDictType)(nil) + err = dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictType).WherePri(input.DictId).Scan(&dictType) + if err != nil { + return errors.New("获取字典类型失败") + } + if dictType == nil { + return errors.New("字典类型不存在") + } + + //修改字典类型 + _, err = dao.SysDictType.Ctx(ctx).TX(tx).WherePri(input.DictId).Update(do.SysDictType{ + DictName: input.DictName, + DictType: input.DictType, + Status: input.Status, + UpdatedBy: userId, + Remark: input.Remark, + ModuleClassify: input.ModuleClassify, }) + if err != nil { + return errors.New("修改字典类型失败") + } + //修改字典数据 + _, err = dao.SysDictData.Ctx(ctx).TX(tx).Data(do.SysDictData{DictType: input.DictType}). + Where(dao.SysDictData.Columns().DictType, dictType.DictType).Update() + if err != nil { + return errors.New("修改字典数据失败") + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) + return err }) return } func (s *sDictType) Get(ctx context.Context, req *common.DictTypeGetReq) (dictType *model.SysDictTypeOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = dao.SysDictType.Ctx(ctx).Where(dao.SysDictType.Columns().DictId, req.DictId).Scan(&dictType) - liberr.ErrIsNil(ctx, err, "获取字典类型失败") - }) + err = dao.SysDictType.Ctx(ctx).Where(dao.SysDictType.Columns().DictId, req.DictId).Scan(&dictType) + if err != nil { + return nil, errors.New("修改字典数据失败") + } + return } // ExistsDictType 检查类型是否已经存在 func (s *sDictType) ExistsDictType(ctx context.Context, dictType string, dictId ...int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictId). - Where(dao.SysDictType.Columns().DictType, dictType) - if len(dictId) > 0 { - m = m.Where(dao.SysDictType.Columns().DictId+" !=? ", dictId[0]) - } - res, e := m.One() - liberr.ErrIsNil(ctx, e, "sql err") - if !res.IsEmpty() { - liberr.ErrIsNil(ctx, gerror.New("字典类型已存在")) - } - }) + m := dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictId). + Where(dao.SysDictType.Columns().DictType, dictType) + if len(dictId) > 0 { + m = m.Where(dao.SysDictType.Columns().DictId+" !=? ", dictId[0]) + } + res, err := m.One() + if err != nil { + return + } + if !res.IsEmpty() { + return errors.New("字典类型已存在") + } + return } // Delete 删除字典类型 func (s *sDictType) Delete(ctx context.Context, dictIds []int) (err error) { - err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - err = g.Try(ctx, func(ctx context.Context) { - discs := ([]*entity.SysDictType)(nil) - err = dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictType). - Where(dao.SysDictType.Columns().DictId+" in (?) ", dictIds).Scan(&discs) - liberr.ErrIsNil(ctx, err, "删除失败") - types := garray.NewStrArray() - for _, dt := range discs { - types.Append(dt.DictType) + err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + discs := ([]*entity.SysDictType)(nil) + err = dao.SysDictType.Ctx(ctx).Fields(dao.SysDictType.Columns().DictType). + Where(dao.SysDictType.Columns().DictId+" in (?) ", dictIds).Scan(&discs) + if err != nil { + return errors.New("删除失败") + } + types := garray.NewStrArray() + for _, dt := range discs { + types.Append(dt.DictType) + } + if types.Len() > 0 { + _, err = dao.SysDictType.Ctx(ctx).TX(tx).Delete(dao.SysDictType.Columns().DictId+" in (?) ", dictIds) + if err != nil { + return errors.New("删除类型失败") } - if types.Len() > 0 { - _, err = dao.SysDictType.Ctx(ctx).TX(tx).Delete(dao.SysDictType.Columns().DictId+" in (?) ", dictIds) - liberr.ErrIsNil(ctx, err, "删除类型失败") - _, err = dao.SysDictData.Ctx(ctx).TX(tx).Delete(dao.SysDictData.Columns().DictType+" in (?) ", types.Slice()) - liberr.ErrIsNil(ctx, err, "删除字典数据失败") + _, err = dao.SysDictData.Ctx(ctx).TX(tx).Delete(dao.SysDictData.Columns().DictType+" in (?) ", types.Slice()) + if err != nil { + return errors.New("删除字典数据失败") } - //清除缓存 - Cache().RemoveByTag(ctx, consts.CacheSysDictTag) - }) + } + //清除缓存 + _, err = cache.Instance().Remove(ctx, consts.CacheSysDictTag) return err }) return diff --git a/internal/logic/common/pg_sequences.go b/internal/logic/common/pg_sequences.go new file mode 100644 index 0000000..6cd4476 --- /dev/null +++ b/internal/logic/common/pg_sequences.go @@ -0,0 +1,43 @@ +package common + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/model" + "sagooiot/internal/service" +) + +type sPgSequences struct { +} + +func PgSequences() *sPgSequences { + return &sPgSequences{} +} + +func init() { + service.RegisterPgSequences(PgSequences()) +} + +// GetPgSequences 获取PG指定表序列信息 +func (s *sPgSequences) GetPgSequences(ctx context.Context, tableName string, primaryKey string) (out *model.PgSequenceOut, err error) { + if tableName == "" { + err = gerror.New("表名不能为空") + return + } + if primaryKey == "" { + err = gerror.New("主键名不能为空") + return + } + // 创建数据库连接 + db := g.DB() + result, err := db.Query(ctx, "SELECT schemaname As schemaName, sequencename AS seqUesCeName, sequenceowner AS seqUesCeOwner,data_type AS dataType,start_value AS startVale,min_value AS minValue,max_value AS maxValue,increment_by AS incrementBy,cycle AS cycle,cache_size AS cacheSize,last_value AS lastVale FROM pg_sequences WHERE sequencename = '"+tableName+"_"+primaryKey+"_seq'") + if err != nil { + return + } + if err = gconv.Scan(result[0], &out); err != nil { + return + } + return +} diff --git a/internal/logic/common/sequences.go b/internal/logic/common/sequences.go new file mode 100644 index 0000000..10a07bf --- /dev/null +++ b/internal/logic/common/sequences.go @@ -0,0 +1,47 @@ +package common + +import ( + "context" + "database/sql" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gerror" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "strings" +) + +type sSequences struct { +} + +func Sequences() *sSequences { + return &sSequences{} +} + +func init() { + service.RegisterSequences(Sequences()) +} + +// GetSequences 获取主键ID +func (s *sSequences) GetSequences(ctx context.Context, result sql.Result, tableName string, primaryKey string) (lastInsertId int64, err error) { + //获取数据源类型 + //TODO 多数据源情况下需对此进行更改优化 + databaseType := gdb.GetConfig(consts.DataBaseGroup)[0].Type + if strings.EqualFold(databaseType, consts.DatabaseTypeMysql) { + //获取自增主键 + lastInsertId, _ = result.LastInsertId() + } else if strings.EqualFold(databaseType, consts.DatabaseTypePostgresql) { + //获取当前表序列 + var pgSequenceOut *model.PgSequenceOut + pgSequenceOut, err = service.PgSequences().GetPgSequences(ctx, tableName, primaryKey) + if err != nil { + return + } + if pgSequenceOut != nil { + lastInsertId = pgSequenceOut.LastVale + } + } else { + err = gerror.New("暂不支持" + databaseType + "的获取方式") + } + return +} diff --git a/internal/logic/common/sys_info.go b/internal/logic/common/sys_info.go new file mode 100644 index 0000000..44037e0 --- /dev/null +++ b/internal/logic/common/sys_info.go @@ -0,0 +1,142 @@ +package common + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/encoding/gbase64" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "math/rand" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/utility/utils" + "sagooiot/pkg/utility/version" + "strings" + "time" +) + +type sSysInfo struct { +} + +func sysInfo() *sSysInfo { + return &sSysInfo{} +} + +func init() { + service.RegisterSysInfo(sysInfo()) +} + +func (s *sSysInfo) GetSysInfo(ctx context.Context) (out g.Map, err error) { + cfgSystemName, err := service.ConfigData().GetConfigByKey(ctx, consts.SysSystemName) + systemName := "沙果IOT" + if cfgSystemName != nil { + systemName = cfgSystemName.ConfigValue + } + + cfgSystemCopyright, err := service.ConfigData().GetConfigByKey(ctx, consts.SysSystemCopyright) + systemCopyright := "Sagoo inc." + if cfgSystemName != nil { + systemCopyright = cfgSystemCopyright.ConfigValue + } + + cfgSystemLogo, err := service.ConfigData().GetConfigByKey(ctx, consts.SysSystemLogo) + systemLogo := "" + if cfgSystemLogo != nil { + systemLogo = cfgSystemLogo.ConfigValue + } + cfgSystemLogoMini, err := service.ConfigData().GetConfigByKey(ctx, consts.SysSystemLogoMini) + systemLogoMini := "" + if cfgSystemLogoMini != nil { + systemLogoMini = cfgSystemLogoMini.ConfigValue + } + cfgSystemLoginPic, err := service.ConfigData().GetConfigByKey(ctx, consts.SysSystemLoginPic) + systemLoginPic := "" + if cfgSystemLoginPic != nil { + systemLoginPic = cfgSystemLoginPic.ConfigValue + } + cfgHomePageRoute, err := service.ConfigData().GetConfigByKey(ctx, consts.HomePageRoute) + systemHomePageRoute := "" + if cfgHomePageRoute != nil { + systemHomePageRoute = cfgHomePageRoute.ConfigValue + } + + cfgSysPasswordChangePeriod, err := service.ConfigData().GetConfigByKey(ctx, consts.SysPasswordChangePeriod) + sysPasswordChangePeriod := "90" + if cfgSysPasswordChangePeriod != nil { + sysPasswordChangePeriod = cfgSysPasswordChangePeriod.ConfigValue + } + + cfgSysIsSecurityControlEnabled, err := service.ConfigData().GetConfigByKey(ctx, consts.SysIsSecurityControlEnabled) + isSecurityControlEnabled := "0" + if cfgSysIsSecurityControlEnabled != nil { + isSecurityControlEnabled = cfgSysIsSecurityControlEnabled.ConfigValue + } + + cfgSysIsRsaEnabled, err := service.ConfigData().GetConfigByKey(ctx, consts.SysIsRsaEnabled) + isRsaEnabled := "0" + if cfgSysIsRsaEnabled != nil { + isRsaEnabled = cfgSysIsRsaEnabled.ConfigValue + } + + out = g.Map{ + "systemName": systemName, + "systemCopyright": systemCopyright, + "systemLogo": systemLogo, + "systemLogoMini": systemLogoMini, + "systemLoginPIC": systemLoginPic, + "systemHomePageRoute": systemHomePageRoute, + + "buildVersion": version.BuildVersion, + "buildTime": version.BuildTime, + "commitID": version.CommitID, + "target": gbase64.EncodeToString([]byte(sysPasswordChangePeriod + "|" + isSecurityControlEnabled + "|" + isRsaEnabled + "|SAGOOIOT")), + } + return +} + +// ServerInfoEscalation 客户端服务信息上报 +func (s *sSysInfo) ServerInfoEscalation(ctx context.Context) (err error) { + num := rand.Intn(10) + time.Sleep(time.Duration(num) * time.Second) + + ip, err := utils.GetLocalIP() + if err != nil { + err = gerror.New("获取客户端信息失败") + return + } + var tmpData *gvar.Var + tmpData, err = cache.Instance().Get(ctx, consts.CacheServerInfo) + var serverInfos []map[string]interface{} + if tmpData.Val() != nil { + err = json.Unmarshal([]byte(tmpData.Val().(string)), &serverInfos) + if err != nil { + err = gerror.New("解析已上报客户端信息失败") + return + } + } + //判断IP是否存在 + isExist := false + if serverInfos != nil && len(serverInfos) > 0 { + for _, serverInfo := range serverInfos { + if strings.EqualFold(serverInfo["ip"].(string), ip) { + isExist = true + //重新启动时间 + serverInfo["date"] = gtime.Now() + break + } + + } + } + if !isExist { + serverInfo := make(map[string]interface{}) + serverInfo["ip"] = ip + serverInfo["date"] = gtime.Now() + serverInfos = append(serverInfos, serverInfo) + } + err = cache.Instance().Set(ctx, consts.CacheServerInfo, serverInfos, 0) + + return +} diff --git a/internal/logic/common/upload.go b/internal/logic/common/upload.go index fbb3e01..225386b 100644 --- a/internal/logic/common/upload.go +++ b/internal/logic/common/upload.go @@ -13,15 +13,18 @@ import ( "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" "github.com/tencentyun/cos-go-sdk-v5" "github.com/tencentyun/cos-go-sdk-v5/debug" "io" + "log" "net/http" "net/url" + "sagooiot/api/v1/common" + "sagooiot/internal/consts" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" "strconv" "strings" "time" @@ -38,7 +41,7 @@ func init() { service.RegisterUpload(upload()) } -//UploadFiles 上传多文件 +// UploadFiles 上传多文件 func (s *sUpload) UploadFiles(ctx context.Context, files []*ghttp.UploadFile, checkFileType string, source int) (result common.UploadMultipleRes, err error) { for _, item := range files { f, e := s.UploadFile(ctx, item, checkFileType, source) @@ -50,7 +53,7 @@ func (s *sUpload) UploadFiles(ctx context.Context, files []*ghttp.UploadFile, ch return } -//UploadFile 上传单文件 +// UploadFile 上传单文件 func (s *sUpload) UploadFile(ctx context.Context, file *ghttp.UploadFile, checkFileType string, source int) (result common.UploadResponse, err error) { // 检查文件类型 @@ -66,9 +69,9 @@ func (s *sUpload) UploadFile(ctx context.Context, file *ghttp.UploadFile, checkF } // 非图片文件只能上传至本地 - if checkFileType == consts.CheckFileTypeFile { + /*if checkFileType == consts.CheckFileTypeFile { source = consts.SourceLocal - } + }*/ switch source { // 上传至本地 @@ -77,6 +80,9 @@ func (s *sUpload) UploadFile(ctx context.Context, file *ghttp.UploadFile, checkF // 上传至腾讯云 case consts.SourceTencent: result, err = s.UploadTencent(ctx, file) + //上传MinIO + case consts.SourceMinio: + result, err = s.UploadMinIO(ctx, file) default: err = errors.New("source参数错误") } @@ -87,7 +93,7 @@ func (s *sUpload) UploadFile(ctx context.Context, file *ghttp.UploadFile, checkF return } -//UploadTencent 上传至腾讯云 +// UploadTencent 上传至腾讯云 func (s *sUpload) UploadTencent(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) { v, err := g.Cfg().Get(ctx, "upload.tencentCOS") if err != nil { @@ -130,7 +136,11 @@ func (s *sUpload) UploadTencent(ctx context.Context, file *ghttp.UploadFile) (re if err != nil { return } - defer f.Close() + defer func(f io.ReadCloser) { + if err = f.Close(); err != nil { + fmt.Println(err) + } + }(f) _, err = client.Object.Put(context.Background(), path, f, opt) result = common.UploadResponse{ Size: file.Size, @@ -142,14 +152,34 @@ func (s *sUpload) UploadTencent(ctx context.Context, file *ghttp.UploadFile) (re return } -//UploadLocal 上传本地 +// UploadLocal 上传本地 func (s *sUpload) UploadLocal(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) { if file == nil { err = errors.New("文件必须") return } - r := g.RequestFromCtx(ctx) - urlPerfix := fmt.Sprintf("http://%s/", r.Host) + + /*r := g.RequestFromCtx(ctx) + + proto := "http" + if strings.Contains(r.Proto, "https") { + proto = "https" + } + host := r.Host + + urlPerfix := fmt.Sprintf("%s://%s/", proto, host)*/ + //获取本地上传域名 + configDataInfo, err := service.ConfigData().GetByKey(ctx, consts.SysUploadFileDomain) + if err != nil { + return + } + if configDataInfo == nil { + err = gerror.New("未配置本地上传域名,无法上传,请联系管理员") + return + } + + urlPerfix := configDataInfo.ConfigValue + p := strings.Trim(consts.UploadPath, "/") sp := s.getStaticPath(ctx) if sp != "" { @@ -168,14 +198,14 @@ func (s *sUpload) UploadLocal(ctx context.Context, file *ghttp.UploadFile) (resu result = common.UploadResponse{ Size: file.Size, Path: fullPath, - FullPath: urlPerfix + fullPath, + FullPath: urlPerfix + "/" + fullPath, Name: file.Filename, Type: file.Header.Get("Content-type"), } return } -//CheckSize 检查上传文件大小 +// CheckSize 检查上传文件大小 func (s *sUpload) CheckSize(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) { var ( @@ -212,7 +242,7 @@ func (s *sUpload) CheckSize(ctx context.Context, checkFileType string, file *ght return } -//CheckType 检查上传文件类型 +// CheckType 检查上传文件类型 func (s *sUpload) CheckType(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) { var ( @@ -244,7 +274,7 @@ func (s *sUpload) CheckType(ctx context.Context, checkFileType string, file *ght return } -//getUpConfig 获取上传配置 +// getUpConfig 获取上传配置 func (s *sUpload) getUpConfig(ctx context.Context, key string) (config *entity.SysConfig, err error) { config, err = sysConfigDataNew().GetConfigByKey(ctx, key) if err != nil { @@ -257,7 +287,7 @@ func (s *sUpload) getUpConfig(ctx context.Context, key string) (config *entity.S return } -//checkFileType 判断上传文件类型是否合法 +// checkFileType 判断上传文件类型是否合法 func (s *sUpload) checkFileType(fileName, typeString string) bool { suffix := gstr.SubStrRune(fileName, gstr.PosRRune(fileName, ".")+1, gstr.LenRune(fileName)-1) imageType := gstr.Split(typeString, ",") @@ -271,7 +301,7 @@ func (s *sUpload) checkFileType(fileName, typeString string) bool { return rightType } -//checkSize 检查文件大小是否合法 +// checkSize 检查文件大小是否合法 func (s *sUpload) checkSize(configSize string, fileSize int64) (bool, error) { match, err := gregex.MatchString(`^([0-9]+)(?i:([a-z]*))$`, configSize) if err != nil { @@ -297,7 +327,7 @@ func (s *sUpload) checkSize(configSize string, fileSize int64) (bool, error) { return cfSize >= fileSize, nil } -//getStaticPath 静态文件夹目录 +// getStaticPath 静态文件夹目录 func (s *sUpload) getStaticPath(ctx context.Context) string { value, _ := g.Cfg().Get(ctx, "server.serverRoot") if !value.IsEmpty() { @@ -305,3 +335,108 @@ func (s *sUpload) getStaticPath(ctx context.Context) string { } return "" } + +// UploadMinIO 上传至MinIO +func (s *sUpload) UploadMinIO(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) { + //获取minio系统参数配置 + var keys = []string{consts.MinioDomain, consts.MinioAccessKeyId, consts.MinioSecretAccessKey, + consts.MinioUseSsl, consts.MinioBucketName, consts.MinioLocation, consts.MinioApiDomain} + configDataInfos, err := service.ConfigData().GetByKeys(ctx, keys) + if err != nil { + return + } + if configDataInfos == nil || len(configDataInfos) == 0 { + err = gerror.New("无MinIO配置,请联系管理员") + return + } + var endpoint string + var accessKeyID string + var secretAccessKey string + var useSSL bool + var bucketName string + var location string + var apiDomain string + for _, info := range configDataInfos { + if strings.EqualFold(info.ConfigKey, consts.MinioDomain) { + endpoint = info.ConfigValue + } + if strings.EqualFold(info.ConfigKey, consts.MinioApiDomain) { + apiDomain = info.ConfigValue + } + if strings.EqualFold(info.ConfigKey, consts.MinioAccessKeyId) { + accessKeyID = info.ConfigValue + } + if strings.EqualFold(info.ConfigKey, consts.MinioSecretAccessKey) { + secretAccessKey = info.ConfigValue + } + if strings.EqualFold(info.ConfigKey, consts.MinioUseSsl) { + useSSL = gconv.Bool(info.ConfigValue) + } + if strings.EqualFold(info.ConfigKey, consts.MinioBucketName) { + bucketName = info.ConfigValue + } + if strings.EqualFold(info.ConfigKey, consts.MinioLocation) { + location = info.ConfigValue + } + } + // Initialize minio client object. + minioClient, err := minio.New(endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: useSSL, + }) + if err != nil { + return + } + + err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location}) + if err != nil { + // Check to see if we already own this bucket (which happens if you run this twice) + var exists bool + exists, err = minioClient.BucketExists(ctx, bucketName) + if err != nil { + return + } + if !exists { + err = gerror.Newf("MinIO不存在%s,请联系管理员", bucketName) + return + } + } + + nowData := time.Now().Format("2006-01-02") + + suffix := gstr.SubStrRune(file.Filename, gstr.PosRRune(file.Filename, ".")+1, gstr.LenRune(file.Filename)-1) + + fileName := grand.S(32) + "." + suffix + // Upload the zip file + objectName := nowData + "/" + fileName + + contentType := file.FileHeader.Header.Get("Content-Type") + + var f io.ReadCloser + f, err = file.Open() + if err != nil { + return + } + defer func(f io.ReadCloser) { + if err = f.Close(); err != nil { + fmt.Println(err) + } + }(f) + + // Upload the zip file with FPutObject + info, err := minioClient.PutObject(ctx, bucketName, objectName, f, file.Size, minio.PutObjectOptions{ContentType: contentType}) + if err != nil { + return + } + + log.Printf("Successfully uploaded %s of size %d\n", objectName, info.Size) + + result = common.UploadResponse{ + Size: info.Size, + Path: bucketName + "/" + info.Key, + FullPath: apiDomain + "/" + bucketName + "/" + info.Key, + Name: fileName, + Type: file.Header.Get("Content-type"), + } + return +} diff --git a/internal/logic/context/context.go b/internal/logic/context/context.go index 4de8eb3..40a4f48 100644 --- a/internal/logic/context/context.go +++ b/internal/logic/context/context.go @@ -3,9 +3,9 @@ package context import ( "context" "github.com/gogf/gf/v2/net/ghttp" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" ) type sContext struct { @@ -77,6 +77,15 @@ func (s *sContext) GetChildrenDeptId(ctx context.Context) []int { return nil } +// GetUserName 获取当前登录用户账户 +func (s *sContext) GetUserName(ctx context.Context) string { + user := s.GetLoginUser(ctx) + if user != nil { + return user.UserName + } + return "" +} + // GetRequestWay 获取当前系统请求方式 func (s *sContext) GetRequestWay(ctx context.Context) string { user := s.GetLoginUser(ctx) diff --git a/internal/logic/datahub/data_node.go b/internal/logic/datahub/data_node.go deleted file mode 100644 index 003046c..0000000 --- a/internal/logic/datahub/data_node.go +++ /dev/null @@ -1,196 +0,0 @@ -package datahub - -import ( - "context" - "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -type sDataNode struct{} - -func init() { - service.RegisterDataNode(dataNodeNew()) -} - -func dataNodeNew() *sDataNode { - return &sDataNode{} -} - -func (s *sDataNode) Add(ctx context.Context, in *model.DataNodeAddInput) (err error) { - id, _ := dao.DataNode.Ctx(ctx). - Fields(dao.DataNode.Columns().NodeId). - Where(dao.DataNode.Columns().SourceId, in.SourceId). - Where(dao.DataNode.Columns().Key, in.Key). - Value() - if id.Int64() > 0 { - return gerror.New("数据节点标识重复") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataNode - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - err = dao.DataNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - rs, err := dao.DataNode.Ctx(ctx).Data(param).Insert() - if err != nil { - return err - } - - nodeId, _ := rs.LastInsertId() - - dataSource, _ := service.DataSource().Detail(ctx, in.SourceId) - if dataSource != nil && dataSource.DataTable != "" { - // 表结构已存在,字段新增 - err = addColumn(ctx, uint64(nodeId)) - if err != nil { - return err - } - } - - return nil - }) - - return -} - -func (s *sDataNode) Edit(ctx context.Context, in *model.DataNodeEditInput) (err error) { - id, _ := dao.DataNode.Ctx(ctx). - Fields(dao.DataNode.Columns().NodeId). - Where(dao.DataNode.Columns().NodeId, in.NodeId). - Value() - if id.Int64() == 0 { - return gerror.New("数据节点不存在") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataNode - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.UpdateBy = uint(loginUserId) - param.NodeId = nil - - _, err = dao.DataNode.Ctx(ctx).Data(param).Where(dao.DataNode.Columns().NodeId, in.NodeId).Update() - - return -} - -func (s *sDataNode) Del(ctx context.Context, nodeId uint64) (err error) { - var p *entity.DataNode - err = dao.DataNode.Ctx(ctx).Where(dao.DataNode.Columns().NodeId, nodeId).Scan(&p) - if p == nil { - return gerror.New("数据节点不存在") - } - - var ds *entity.DataSource - err = dao.DataSource.Ctx(ctx).Where(dao.DataSource.Columns().SourceId, p.SourceId).Scan(&ds) - if err != nil { - return - } - if ds != nil && ds.Status != model.DataSourceStatusOff { - return gerror.New("数据源已发布,请先撤回,再删除") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - if ds != nil && ds.DataTable != "" { - // 表结构已存在,字段须删除处理 - if err = dropColumn(ctx, nodeId); err != nil { - return err - } - } - - _, err = dao.DataNode.Ctx(ctx). - Data(do.DataNode{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataNode.Columns().NodeId, nodeId). - Unscoped(). - Update() - return err - }) - - return -} - -func (s *sDataNode) List(ctx context.Context, sourceId uint64) (list []*model.DataNodeOutput, err error) { - var p []*entity.DataNode - err = dao.DataNode.Ctx(ctx).OrderAsc(dao.DataNode.Columns().NodeId).Where(dao.DataNode.Columns().SourceId, sourceId).Scan(&p) - if err != nil || p == nil { - return - } - - list = make([]*model.DataNodeOutput, len(p)) - for i, v := range p { - // 规则配置 - var rule []*model.DataSourceRule - if v.Rule != "" { - j, _ := gjson.DecodeToJson(v.Rule) - if err = j.Scan(&rule); err != nil { - return nil, err - } - } - - out := new(model.DataNodeOutput) - out.DataNode = v - out.NodeRule = rule - - list[i] = out - } - - return -} - -// 详情 -func (s *sDataNode) Detail(ctx context.Context, nodeId uint64) (out *model.DataNodeOutput, err error) { - var p *entity.DataNode - err = dao.DataNode.Ctx(ctx).Where(dao.DataNode.Columns().NodeId, nodeId).Scan(&p) - if err != nil || p == nil { - return - } - - // 规则配置 - var rule []*model.DataSourceRule - if p.Rule != "" { - j, _ := gjson.DecodeToJson(p.Rule) - if err = j.Scan(&rule); err != nil { - return nil, err - } - } - - out = new(model.DataNodeOutput) - out.DataNode = p - out.NodeRule = rule - - return -} diff --git a/internal/logic/datahub/data_source.go b/internal/logic/datahub/data_source.go deleted file mode 100644 index 6af9a81..0000000 --- a/internal/logic/datahub/data_source.go +++ /dev/null @@ -1,624 +0,0 @@ -package datahub - -import ( - "context" - "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/text/gstr" - "github.com/gogf/gf/v2/util/gconv" -) - -type sDataSource struct{} - -func init() { - service.RegisterDataSource(dataSourceNew()) -} - -func dataSourceNew() *sDataSource { - return &sDataSource{} -} - -func (s *sDataSource) Add(ctx context.Context, in *model.DataSourceApiAddInput) (sourceId uint64, err error) { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - param.Status = 0 - param.LockKey = 0 - - in.Config.Url = gstr.TrimAll(in.Config.Url) - - param.Config, err = json.Marshal(in.Config) - if err != nil { - err = gerror.New("数据源配置格式错误") - return - } - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return 0, gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - rs, err := dao.DataSource.Ctx(ctx).Data(param).Insert() - if err != nil { - return - } - - newId, _ := rs.LastInsertId() - sourceId = uint64(newId) - - return -} - -func (s *sDataSource) Edit(ctx context.Context, in *model.DataSourceApiEditInput) (err error) { - out, err := s.Detail(ctx, in.SourceId) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据源不存在") - } - if out.Status == model.DataSourceStatusOn { - return gerror.New("数据源已发布") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.UpdateBy = uint(loginUserId) - param.SourceId = nil - if out.LockKey == 1 { - param.Key = nil - } else { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - WhereNot(dao.DataSource.Columns().SourceId, in.SourceId). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - } - - in.Config.Url = gstr.TrimAll(in.Config.Url) - - param.Config, err = json.Marshal(in.Config) - if err != nil { - return gerror.New("数据源配置格式错误") - } - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - err = dao.DataSource.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = dao.DataSource.Ctx(ctx). - Data(param). - Where(dao.DataSource.Columns().SourceId, in.SourceId). - Update() - if err != nil { - return err - } - - // 同步聚合时间到定时任务管理 - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataSource-" + gconv.String(out.SourceId) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) > 0 { - editJob := new(model.SysJobEditInput) - editJob.JobName = list[0].JobName - editJob.JobParams = list[0].JobParams - editJob.JobGroup = list[0].JobGroup - editJob.InvokeTarget = list[0].InvokeTarget - editJob.CronExpression = in.Config.CronExpression - editJob.MisfirePolicy = list[0].MisfirePolicy - editJob.Concurrent = list[0].Concurrent - editJob.Status = list[0].Status - editJob.Remark = list[0].Remark - - editJob.JobId = list[0].JobId - editJob.UpdateBy = uint64(loginUserId) - err = service.SysJob().EditJob(ctx, editJob) - if err != nil { - return err - } - } - - return nil - }) - - return -} - -func (s *sDataSource) Del(ctx context.Context, ids []uint64) (err error) { - var p []*entity.DataSource - err = dao.DataSource.Ctx(ctx).WhereIn(dao.DataSource.Columns().SourceId, ids).Scan(&p) - if len(p) == 0 { - return gerror.New("数据源不存在") - } - if len(p) == 1 && p[0].Status == model.DataSourceStatusOn { - return gerror.New("数据源已发布,请先撤回,再删除") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 删除数据源 - var delIds []uint64 - for _, id := range ids { - rs, err := dao.DataSource.Ctx(ctx). - Data(do.DataSource{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataSource.Columns().SourceId, id). - Where(dao.DataSource.Columns().Status, model.DataSourceStatusOff). - Unscoped(). - Update() - if err != nil { - return err - } - - num, _ := rs.RowsAffected() - if num > 0 { - delIds = append(delIds, id) - } - } - - // 删除数据节点 - for _, id := range delIds { - _, err = dao.DataNode.Ctx(ctx). - Data(do.DataNode{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataNode.Columns().SourceId, id). - Unscoped(). - Update() - if err != nil { - return err - } - } - - // 删除定时任务、删除数据表 - for _, id := range delIds { - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataSource-" + gconv.String(id) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) != 0 { - err = service.SysJob().DeleteJobByIds(ctx, []int{int(list[0].JobId)}) - } - - err = dropTable(ctx, id) - if err != nil { - return err - } - } - - return nil - }) - - return -} - -func (s *sDataSource) Search(ctx context.Context, in *model.DataSourceSearchInput) (out *model.DataSourceSearchOutput, err error) { - out = new(model.DataSourceSearchOutput) - c := dao.DataSource.Columns() - m := dao.DataSource.Ctx(ctx).OrderDesc(c.SourceId) - - if in.Key != "" { - m = m.Where(c.Key, in.Key) - } - if in.Name != "" { - m = m.WhereLike(c.Name, "%"+in.Name+"%") - } - if in.From > 0 { - m = m.Where(c.From, in.From) - } - - out.Total, _ = m.Count() - out.CurrentPage = in.PageNum - err = m.Page(in.PageNum, in.PageSize).Scan(&out.List) - - return -} - -// 已发布源列表 -func (s *sDataSource) List(ctx context.Context) (list []*entity.DataSource, err error) { - err = dao.DataSource.Ctx(ctx). - Where(dao.DataSource.Columns().Status, model.DataSourceStatusOn). - OrderDesc(dao.DataSource.Columns().SourceId). - Scan(&list) - return -} - -func (s *sDataSource) Detail(ctx context.Context, sourceId uint64) (out *model.DataSourceOutput, err error) { - var p *entity.DataSource - err = dao.DataSource.Ctx(ctx).Where(dao.DataSource.Columns().SourceId, sourceId).Scan(&p) - if err != nil { - return - } - if p == nil { - return - } - - // 规则配置 - var rule []*model.DataSourceRule - if p.Rule != "" { - j, _ := gjson.DecodeToJson(p.Rule) - if err = j.Scan(&rule); err != nil { - return nil, err - } - } - - out = new(model.DataSourceOutput) - out.DataSource = p - out.SourceRule = rule - - // 数据源配置 - if p.Config != "" { - j, _ := gjson.DecodeToJson(p.Config) - switch p.From { - case model.DataSourceFromApi: - // api 配置 - out.ApiConfig = &model.DataSourceConfigApi{} - err = j.Scan(out.ApiConfig) - case model.DataSourceFromDevice: - // 设备配置 - out.DeviceConfig = &model.DataSourceConfigDevice{} - err = j.Scan(out.DeviceConfig) - case model.DataSourceFromDb: - // 数据库配置 - out.DbConfig = &model.DataSourceConfigDb{} - err = j.Scan(out.DbConfig) - } - } - - return -} - -func (s *sDataSource) Deploy(ctx context.Context, sourceId uint64) (err error) { - out, err := s.Detail(ctx, sourceId) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据源不存在") - } - if out.Status == model.DataSourceStatusOn { - return gerror.New("数据源已发布") - } - - // 获取节点 - nodeList, err := service.DataNode().List(ctx, sourceId) - if err != nil { - return - } - if len(nodeList) == 0 { - err = gerror.New("该数据源还未创建数据节点") - return - } - - // 获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataSource.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 创建表结构 - table := "" - if out.From == model.DataSourceFromApi || out.From == model.DataSourceFromDb { - if out.DataTable == "" { - table, err = createTable(ctx, sourceId) - if err != nil { - return err - } - } - } - - m := g.Map{ - dao.DataSource.Columns().Status: model.DataSourceStatusOn, - dao.DataSource.Columns().LockKey: 1, - } - if table != "" { - m[dao.DataSource.Columns().DataTable] = table - } - - _, err = dao.DataSource.Ctx(ctx). - Data(m). - Where(dao.DataSource.Columns().SourceId, sourceId). - Update() - - return err - }) - if err != nil { - return err - } - - if out.From != model.DataSourceFromApi && out.From != model.DataSourceFromDb { - return - } - - var cron string - switch out.From { - case model.DataSourceFromApi: - cron = out.ApiConfig.CronExpression - case model.DataSourceFromDb: - cron = out.DbConfig.CronExpression - } - - // 开启数据更新任务,任务排重 -Job: - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataSource-" + gconv.String(sourceId) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) == 0 { - sysJob := new(model.SysJobAddInput) - sysJob.JobName = "dataSource-" + gconv.String(sourceId) - sysJob.JobParams = gconv.String(sourceId) - sysJob.JobGroup = "dataSourceJob" - sysJob.InvokeTarget = "dataSource" - sysJob.CronExpression = cron - sysJob.MisfirePolicy = 1 - sysJob.Concurrent = 0 - sysJob.Status = 0 - sysJob.Remark = out.Name - sysJob.CreateBy = uint64(loginUserId) - err = service.SysJob().AddJob(ctx, sysJob) - goto Job - } - err = service.SysJob().JobStart(ctx, list[0]) - // 初始执行一次 - err = service.SysJob().JobRun(ctx, list[0]) - - return -} - -func (s *sDataSource) Undeploy(ctx context.Context, sourceId uint64) (err error) { - out, err := s.Detail(ctx, sourceId) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据源不存在") - } - if out.Status == model.DataSourceStatusOff { - return gerror.New("数据源已停用") - } - - _, err = dao.DataSource.Ctx(ctx). - Data(g.Map{dao.DataSource.Columns().Status: model.DataSourceStatusOff}). - Where(dao.DataSource.Columns().SourceId, sourceId). - Update() - if err != nil { - return err - } - - if out.From != model.DataSourceFromApi && out.From != model.DataSourceFromDb { - return - } - - // 关闭数据更新任务 - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataSource-" + gconv.String(sourceId) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) == 0 { - return - } - err = service.SysJob().JobStop(ctx, list[0]) - - return -} - -// 更新数据记录,定时任务触发 -func (s *sDataSource) UpdateData(ctx context.Context, sourceId uint64) (err error) { - out, err := s.Detail(ctx, sourceId) - if err != nil { - return - } - if out == nil { - err = gerror.New("数据源不存在") - return - } - - switch out.From { - case model.DataSourceFromApi: - err = s.updateDataForApi(ctx, sourceId) - case model.DataSourceFromDb: - err = s.updateDataForDb(ctx, sourceId) - } - return -} - -// 获取数据源的聚合数据 -func (s *sDataSource) GetData(ctx context.Context, in *model.DataSourceDataInput) (out *model.DataSourceDataOutput, err error) { - src, err := s.Detail(ctx, in.SourceId) - if err != nil { - return - } - if src == nil { - err = gerror.New("数据源不存在") - return - } - if src.Status == model.DataSourceStatusOff { - err = gerror.New("数据源已停用") - return - } - - switch src.From { - case model.DataSourceFromApi: - out, err = s.getApiDataRecord(ctx, in, src) - case model.DataSourceFromDevice: - out, err = s.getDeviceDataRecord(ctx, in, src) - case model.DataSourceFromDb: - out, err = s.getDbDataRecord(ctx, in, src) - } - - return -} - -// 获取数据源的聚合数据,非分页 -func (s *sDataSource) GetAllData(ctx context.Context, in *model.SourceDataAllInput) (out *model.SourceDataAllOutput, err error) { - ds, err := s.Detail(ctx, in.SourceId) - if err != nil { - return - } - if ds == nil { - err = gerror.New("数据源不存在") - return - } - if ds.Status == model.DataSourceStatusOff { - err = gerror.New("数据源已停用") - return - } - - switch ds.From { - case model.DataSourceFromApi: - out, err = s.getApiDataAllRecord(ctx, in, ds) - case model.DataSourceFromDevice: - out, err = s.getDeviceDataAllRecord(ctx, in, ds) - case model.DataSourceFromDb: - out, err = s.getDbDataAllRecord(ctx, in, ds) - } - - return -} - -// 数据源获取数据的内网方法列表,供大屏使用 -func (s *sDataSource) AllSource(ctx context.Context) (out []*model.AllSourceOut, err error) { - err = dao.DataSource.Ctx(ctx). - Where(dao.DataSource.Columns().Status, model.DataSourceStatusOn). - OrderDesc(dao.DataSource.Columns().SourceId). - Scan(&out) - if err != nil { - return - } - - for _, v := range out { - v.Path = "GetAllData?pageNum=1&pageSize=10&sourceId=" + gconv.String(v.SourceId) - } - - return -} - -// 复制数据源 -func (s *sDataSource) CopeSource(ctx context.Context, sourceId uint64) (err error) { - out, err := s.Detail(ctx, sourceId) - if err != nil { - return - } - if out == nil { - err = gerror.New("数据源不存在") - return - } - - switch out.From { - case model.DataSourceFromApi: - _, err = s.copeApiSource(ctx, out) - case model.DataSourceFromDevice: - _, err = s.copeDeviceSource(ctx, out) - case model.DataSourceFromDb: - _, err = s.copeDbSource(ctx, out) - } - - return -} - -// 复制数据源节点 -func (s *sDataSource) copeNode(ctx context.Context, sourceId, newSourceId uint64) (err error) { - nodes, err := service.DataNode().List(ctx, sourceId) - if err != nil || len(nodes) == 0 { - return - } - - for _, v := range nodes { - var in *model.DataNodeAddInput - err = gconv.Scan(v.DataNode, &in) - if err != nil { - return - } - in.SourceId = newSourceId - - err = gconv.Scan(v.NodeRule, &in.Rule) - if err != nil { - return - } - - err = service.DataNode().Add(ctx, in) - if err != nil { - return - } - } - return -} - -// 更新数据聚合时长 -func (s *sDataSource) UpdateInterval(ctx context.Context, sourceId uint64, cronExpression string) (err error) { - out, _ := s.Detail(ctx, sourceId) - - var c []byte - - switch out.From { - case model.DataSourceFromApi: - conf := out.ApiConfig - conf.CronExpression = cronExpression - c, _ = json.Marshal(conf) - case model.DataSourceFromDb: - conf := out.DbConfig - conf.CronExpression = cronExpression - c, _ = json.Marshal(conf) - } - - _, err = dao.DataSource.Ctx(ctx). - Data(g.Map{ - dao.DataSource.Columns().Config: c, - }). - Where(dao.DataSource.Columns().SourceId, sourceId). - Update() - - return -} diff --git a/internal/logic/datahub/data_source_api.go b/internal/logic/datahub/data_source_api.go deleted file mode 100644 index 298d85e..0000000 --- a/internal/logic/datahub/data_source_api.go +++ /dev/null @@ -1,262 +0,0 @@ -package datahub - -import ( - "context" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "reflect" - "strings" - "sync" - - "github.com/gogf/gf/v2/container/gmap" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/util/gconv" -) - -func (s *sDataSource) updateDataForApi(ctx context.Context, sourceId uint64) error { - // 数据节点 - nodeList, err := service.DataNode().List(ctx, sourceId) - if err != nil { - return err - } - if len(nodeList) == 0 { - return gerror.New("数据源未添加数据节点") - } - - // 获取api数据 - apiDataArr, err := s.GetApiData(ctx, sourceId) - if err != nil { - return err - } - - // 数据映射 - var insertData []*gmap.AnyAnyMap - for _, apiData := range apiDataArr { - j := gjson.New(apiData) - m := gmap.New(true) - - var wg sync.WaitGroup - for _, v := range nodeList { - wg.Add(1) - go func(v *model.DataNodeOutput) { - defer wg.Done() - - // 规则过滤,启用节点规则 - var rule *model.DataSourceRule - if len(v.NodeRule) > 0 { - rule = v.NodeRule[0] - } - - rs := getValue(v.Value, j, rule) - if rs != "" { - m.Set(v.Key, rs) - } - }(v) - } - wg.Wait() - insertData = append(insertData, m) - } - - // 入库 - if len(insertData) > 0 { - table := getTableName(sourceId) - err = g.DB(DataCenter()).GetCore().ClearTableFields(ctx, table) - if err != nil { - return err - } - _, err = g.DB(DataCenter()).Save(ctx, table, insertData) - if err != nil { - return err - } - } - - return nil -} - -// 获取api数据 -func (s *sDataSource) GetApiData(ctx context.Context, sourceId uint64) (apiData []string, err error) { - p, _ := s.Detail(ctx, sourceId) - if p == nil || p.ApiConfig == nil { - err = gerror.New("数据源不存在或未进行配置") - return - } - - // api配置 - config := p.ApiConfig - - // api数据获取 - get := func(header, data g.MapStrStr) (string, error) { - client := g.Client() - client.Header(header) - res, err := client.DoRequest(ctx, config.Method, config.Url, data) - if err != nil { - return "", err - } - defer res.Close() - return res.ReadAllString(), nil - } - - if len(config.RequestParams) > 0 { - for _, param := range config.RequestParams { - header := g.MapStrStr{} - data := g.MapStrStr{} - for _, v := range param { - if v.Type == "header" { - header[v.Key] = v.Value - } else { - data[v.Key] = v.Value - } - } - rs, err := get(header, data) - if err != nil { - return nil, err - } - apiData = append(apiData, rs) - } - } else { - rs, err := get(nil, nil) - if err != nil { - return nil, err - } - apiData = append(apiData, rs) - } - - return -} - -// api数据提取值 -func getValue(value string, apiData *gjson.Json, rule *model.DataSourceRule) string { - sv := apiData.Get(value) - if sv == nil { - return "" - } - - s := sv.String() - - if rule != nil && rule.Expression != "" { - // 正则过滤数据 - rs, _ := gregex.ReplaceString(rule.Expression, rule.Replace, s) - return gconv.String(rs) - } - - return s -} - -// api源数据记录列表 -func (s *sDataSource) getApiDataRecord(ctx context.Context, in *model.DataSourceDataInput, ds *model.DataSourceOutput) (out *model.DataSourceDataOutput, err error) { - out = new(model.DataSourceDataOutput) - - table := getTableName(in.SourceId) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " where " + strings.Join(exp, " and ") - } - sql := "select * from " + table + where + " order by created_at desc" - - out.Total, _ = g.DB(DataCenter()).GetCount(ctx, sql, value...) - out.CurrentPage = in.PageNum - - sql += fmt.Sprintf(" limit %d, %d", (in.PageNum-1)*in.PageSize, in.PageSize) - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - out.List = rs.Json() - - return -} - -// api源数据记录列表,非分页 -func (s *sDataSource) getApiDataAllRecord(ctx context.Context, in *model.SourceDataAllInput, ds *model.DataSourceOutput) (out *model.SourceDataAllOutput, err error) { - out = new(model.SourceDataAllOutput) - - table := getTableName(in.SourceId) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " where " + strings.Join(exp, " and ") - } - sql := "select * from " + table + where + " order by created_at desc" - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - out.List = rs.Json() - - return -} - -// 复制API数据源 -func (s *sDataSource) copeApiSource(ctx context.Context, ds *model.DataSourceOutput) (newSourceId uint64, err error) { - err = dao.DataSource.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 复制源 - in := new(model.DataSourceApiAddInput) - in.DataSource = model.DataSource{} - in.Config = model.DataSourceConfigApi{} - - err = gconv.Scan(ds.DataSource, &in.DataSource) - if err != nil { - return err - } - in.DataSource.Key += "_" + gtime.Now().Format("YmdHis") - - err = gconv.Scan(ds.ApiConfig, &in.Config) - if err != nil { - return err - } - - newSourceId, err = s.Add(ctx, in) - if err != nil { - return err - } - - // 复制节点 - err = s.copeNode(ctx, ds.SourceId, newSourceId) - - return err - }) - - return -} diff --git a/internal/logic/datahub/data_source_db.go b/internal/logic/datahub/data_source_db.go deleted file mode 100644 index fdcdaa3..0000000 --- a/internal/logic/datahub/data_source_db.go +++ /dev/null @@ -1,497 +0,0 @@ -package datahub - -import ( - "context" - "encoding/json" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/service" - "reflect" - "strings" - "sync" - - _ "github.com/gogf/gf/contrib/drivers/mssql/v2" - "github.com/gogf/gf/v2/container/gmap" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/text/gregex" - "github.com/gogf/gf/v2/util/gconv" -) - -// 添加数据库数据源 -func (s *sDataSource) AddDb(ctx context.Context, in *model.DataSourceDbAddInput) (sourceId uint64, err error) { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - param.Status = 0 - param.LockKey = 0 - - conf, err := json.Marshal(in.Config) - if err != nil { - err = gerror.New("数据源配置格式错误") - return - } - param.Config = conf - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return 0, gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - rs, err := dao.DataSource.Ctx(ctx).Data(param).Insert() - if err != nil { - return - } - - newId, _ := rs.LastInsertId() - sourceId = uint64(newId) - - return -} - -// 编辑数据库数据源 -func (s *sDataSource) EditDb(ctx context.Context, in *model.DataSourceDbEditInput) (err error) { - out, err := s.Detail(ctx, in.SourceId) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据源不存在") - } - if out.Status == model.DataSourceStatusOn { - return gerror.New("数据源已发布") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.UpdateBy = uint(loginUserId) - param.SourceId = nil - if out.LockKey == 1 { - param.Key = nil - } else { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - WhereNot(dao.DataSource.Columns().SourceId, in.SourceId). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - } - - conf, err := json.Marshal(in.Config) - if err != nil { - return gerror.New("数据源配置格式错误") - } - param.Config = conf - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - _, err = dao.DataSource.Ctx(ctx).Data(param).Where(dao.DataSource.Columns().SourceId, in.SourceId).Update() - - return -} - -// 获取数据库表结构 -func (s *sDataSource) GetDbFields(ctx context.Context, sourceId uint64) (g.MapStrAny, error) { - p, _ := s.Detail(ctx, sourceId) - if p == nil || p.DbConfig == nil { - return nil, gerror.New("数据源不存在或未进行配置") - } - - // 数据库配置 - conf := p.DbConfig - - db, err := gdb.New(gdb.ConfigNode{ - Host: conf.Host, - Port: gconv.String(conf.Port), - User: conf.User, - Pass: conf.Passwd, - Name: conf.DbName, - Type: conf.Type, - }) - if err != nil { - return nil, err - } - - if conf.QueryType == model.DataSourceDbQueryTypeSql { - rs, err := db.GetOne(ctx, conf.TableName) - if err != nil { - return nil, err - } - - tmp := rs.GMap() - data := make(g.MapStrAny) - for k := range tmp.Map() { - tf := new(gdb.TableField) - tf.Name = k - tf.Comment = k - - switch (tmp.GetVar(k).Interface()).(type) { - case int: - tf.Type = "int" - case string: - tf.Type = "string" - case float64: - tf.Type = "double" - case *gtime.Time: - tf.Type = "date" - default: - tf.Type = "string" - } - data[k] = tf - } - return data, nil - } - - fields, err := db.TableFields(ctx, conf.TableName) - if err != nil { - return nil, err - } - - // 字段类型转换 - data := make(g.MapStrAny, len(fields)) - for k, v := range fields { - p := *v - vtype := strings.Split(p.Type, "(") - p.Type = mappingFieldType(vtype[0]) - data[k] = p - } - return data, nil -} - -// mssql字段类型映射 -func mappingFieldType(t string) (netT string) { - switch t { - case "int", "smallint", "tinyint", "timestamp": - netT = "int" - case "bigint": - netT = "long" - case "float", "decimal", "numeric": - netT = "float" - case "real": - netT = "double" - case "varchar", "char", "nvarchar", "nchar", "bit": - netT = "string" - case "text", "ntext": - netT = "text" - case "datetime", "smalldatetime": - netT = "date" - default: - netT = "string" - } - return -} - -// 获取数据库单条数据 -func (s *sDataSource) GetDbData(ctx context.Context, sourceId uint64) (string, error) { - rs, _, err := s.getDbData(ctx, sourceId, 1) - if err != nil || rs.Len() == 0 { - return "", err - } - return rs[0].Json(), nil -} - -// 获取数据源配置的数据库数据 -// 数据源配置时,注意控制数据获取的数量,数量过大可能造成内存溢出 -func (s *sDataSource) getDbData(ctx context.Context, sourceId uint64, limit int) (rs gdb.Result, ds *model.DataSourceOutput, err error) { - p, _ := s.Detail(ctx, sourceId) - if p == nil || p.DbConfig == nil { - err = gerror.New("数据源不存在或未进行配置") - return - } - ds = p - - // 数据库配置 - conf := p.DbConfig - - dbDebug, _ := g.Cfg().Get(context.TODO(), "database.default.debug") - - db, err := gdb.New(gdb.ConfigNode{ - Host: conf.Host, - Port: gconv.String(conf.Port), - User: conf.User, - Pass: conf.Passwd, - Name: conf.DbName, - Type: conf.Type, - Debug: dbDebug.Bool(), - }) - if err != nil { - return - } - - if conf.QueryType == model.DataSourceDbQueryTypeSql { - sql := conf.TableName - if strings.Contains(sql, "%d") { - sql = fmt.Sprintf(conf.TableName, conf.PkMax) - } - rs, err = db.GetAll(ctx, sql) - return - } - - where := "" - if limit <= 0 { - limit = conf.Num - - if conf.Pk != "" { - // 判断主键类型 - var tmap g.MapStrAny - tmap, err = s.GetDbFields(ctx, sourceId) - if err != nil { - return - } - if t, ok := tmap[conf.Pk]; ok { - tf := t.(gdb.TableField) - switch tf.Type { - case "int", "long": - where = fmt.Sprintf("where %s > %d", conf.Pk, conf.PkMax) - } - } - } - } - order := "" - if conf.Pk != "" { - order = fmt.Sprintf("order by %s asc", conf.Pk) - } - sql := fmt.Sprintf("select * from %s %s %s limit %d", conf.TableName, where, order, limit) - if conf.Type == "mssql" { - sql = fmt.Sprintf("select top %d * from %s %s %s", limit, conf.TableName, where, order) - } - rs, err = db.GetAll(ctx, sql) - return -} - -// 数据库源数据记录列表 -func (s *sDataSource) getDbDataRecord(ctx context.Context, in *model.DataSourceDataInput, ds *model.DataSourceOutput) (out *model.DataSourceDataOutput, err error) { - out = new(model.DataSourceDataOutput) - - table := getTableName(in.SourceId) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " where " + strings.Join(exp, " and ") - } - sql := "select * from " + table + where + " order by created_at desc" - - out.Total, _ = g.DB(DataCenter()).GetCount(ctx, sql, value...) - out.CurrentPage = in.PageNum - - sql += fmt.Sprintf(" limit %d, %d", (in.PageNum-1)*in.PageSize, in.PageSize) - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - out.List = rs.Json() - - return -} - -// 数据库源数据记录列表,非分页 -func (s *sDataSource) getDbDataAllRecord(ctx context.Context, in *model.SourceDataAllInput, ds *model.DataSourceOutput) (out *model.SourceDataAllOutput, err error) { - out = new(model.SourceDataAllOutput) - - table := getTableName(in.SourceId) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " where " + strings.Join(exp, " and ") - } - sql := "select * from " + table + where + " order by created_at desc" - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - out.List = rs.Json() - - return -} - -// 复制数据库数据源 -func (s *sDataSource) copeDbSource(ctx context.Context, ds *model.DataSourceOutput) (newSourceId uint64, err error) { - err = dao.DataSource.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 复制源 - in := new(model.DataSourceDbAddInput) - in.DataSource = model.DataSource{} - in.Config = model.DataSourceConfigDb{} - - err = gconv.Scan(ds.DataSource, &in.DataSource) - if err != nil { - return err - } - in.DataSource.Key += "_" + gtime.Now().Format("YmdHis") - - err = gconv.Scan(ds.DbConfig, &in.Config) - if err != nil { - return err - } - - newSourceId, err = s.AddDb(ctx, in) - if err != nil { - return err - } - - // 复制节点 - err = s.copeNode(ctx, ds.SourceId, newSourceId) - - return err - }) - - return -} - -func (s *sDataSource) updateDataForDb(ctx context.Context, sourceId uint64) error { - // 数据节点 - nodeList, err := service.DataNode().List(ctx, sourceId) - if err != nil { - return err - } - if len(nodeList) == 0 { - return gerror.New("数据源未添加数据节点") - } - - // 获取数据库数据 - rs, ds, err := s.getDbData(ctx, sourceId, -1) - if err != nil { - return err - } - - var pkMax uint64 = 0 - - // 数据映射 - var insertData []*gmap.AnyAnyMap - for _, row := range rs { - pk := row[ds.DbConfig.Pk] - if pk.Uint64() > pkMax { - pkMax = pk.Uint64() - } - - m := gmap.New(true) - - var wg sync.WaitGroup - for _, v := range nodeList { - wg.Add(1) - go func(v *model.DataNodeOutput) { - defer wg.Done() - - // 规则过滤,启用节点规则 - var rule *model.DataSourceRule - if len(v.NodeRule) > 0 { - rule = v.NodeRule[0] - } - - sv, ok := row[v.Value] - if !ok { - return - } - - rs := sv.String() - if rule != nil && rule.Expression != "" { - // 正则过滤数据 - rs, err = gregex.ReplaceString(rule.Expression, rule.Replace, rs) - if err != nil { - return - } - } - m.Set(v.Key, rs) - }(v) - } - wg.Wait() - insertData = append(insertData, m) - } - - // 入库 - if len(insertData) > 0 { - table := getTableName(sourceId) - err = g.DB(DataCenter()).GetCore().ClearTableFields(ctx, table) - if err != nil { - return err - } - _, err = g.DB(DataCenter()).Save(ctx, table, insertData) - if err != nil { - return err - } - } - - // 主键最大值存储 - if pkMax > 0 { - conf := ds.DbConfig - conf.PkMax = pkMax - c, _ := json.Marshal(conf) - _, err = dao.DataSource.Ctx(ctx). - Data(g.Map{ - dao.DataSource.Columns().Config: c, - }). - Where(dao.DataSource.Columns().SourceId, sourceId). - Update() - } - return err -} diff --git a/internal/logic/datahub/data_source_device.go b/internal/logic/datahub/data_source_device.go deleted file mode 100644 index 66360cd..0000000 --- a/internal/logic/datahub/data_source_device.go +++ /dev/null @@ -1,300 +0,0 @@ -package datahub - -import ( - "context" - "encoding/json" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/service" - "reflect" - "strings" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -// 添加设备数据源 -func (s *sDataSource) AddDevice(ctx context.Context, in *model.DataSourceDeviceAddInput) (sourceId uint64, err error) { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - param.Status = 0 - param.LockKey = 0 - - conf, err := json.Marshal(in.Config) - if err != nil { - err = gerror.New("数据源配置格式错误") - return - } - param.Config = conf - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return 0, gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - rs, err := dao.DataSource.Ctx(ctx).Data(param).Insert() - if err != nil { - return - } - - newId, _ := rs.LastInsertId() - sourceId = uint64(newId) - - return -} - -// 编辑设备数据源 -func (s *sDataSource) EditDevice(ctx context.Context, in *model.DataSourceDeviceEditInput) (err error) { - out, err := s.Detail(ctx, in.SourceId) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据源不存在") - } - if out.Status == model.DataSourceStatusOn { - return gerror.New("数据源已发布") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataSource - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.UpdateBy = uint(loginUserId) - param.SourceId = nil - if out.LockKey == 1 { - param.Key = nil - } else { - id, _ := dao.DataSource.Ctx(ctx). - Fields(dao.DataSource.Columns().SourceId). - Where(dao.DataSource.Columns().Key, in.Key). - WhereNot(dao.DataSource.Columns().SourceId, in.SourceId). - Value() - if id.Int64() > 0 { - err = gerror.New("数据源标识重复") - return - } - } - - conf, err := json.Marshal(in.Config) - if err != nil { - return gerror.New("数据源配置格式错误") - } - param.Config = conf - - if in.Rule != nil { - rule, err := json.Marshal(in.Rule) - if err != nil { - return gerror.New("规则配置格式错误") - } - param.Rule = rule - } - - _, err = dao.DataSource.Ctx(ctx).Data(param).Where(dao.DataSource.Columns().SourceId, in.SourceId).Update() - - return -} - -// 获取设备源数据,源配置测试使用 -func (s *sDataSource) GetDeviceData(ctx context.Context, sourceId uint64) (string, error) { - p, _ := s.Detail(ctx, sourceId) - if p == nil || p.DeviceConfig == nil { - return "", gerror.New("数据源不存在或未进行配置") - } - - // 设备配置 - conf := p.DeviceConfig - - // TDengine - sql := "select * from ? where device='?' order by ts desc limit 1" - rs, err := service.TdEngine().GetOne(ctx, sql, conf.ProductKey, conf.DeviceKey) - if err != nil { - return "", err - } - data, _ := json.Marshal(rs) - - return string(data), nil -} - -// 设备源数据记录列表 -func (s *sDataSource) getDeviceDataRecord(ctx context.Context, in *model.DataSourceDataInput, ds *model.DataSourceOutput) (out *model.DataSourceDataOutput, err error) { - out = new(model.DataSourceDataOutput) - - // 设备配置 - conf := ds.DeviceConfig - - // 搜索条件 - var exp []string - if in.Param != nil { - for k, v := range in.Param { - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - tmp := k - for i := 0; i < s.Len(); i++ { - s := fmt.Sprintf("'%v'", v) - tmp = strings.Replace(tmp, "?", s, 1) - } - exp = append(exp, tmp) - } else { - s := fmt.Sprintf("'%v'", v) - exp = append(exp, strings.Replace(k, "?", s, 1)) - } - } - } - where := "" - if len(exp) > 0 { - where = " and " + strings.Join(exp, " and ") - } - - // TDengine - sql := fmt.Sprintf("select count(*) as num from %s where device='%s' %s limit 100", conf.ProductKey, conf.DeviceKey, where) - rs, err := service.TdEngine().GetOne(ctx, sql) - if err != nil { - return - } - out.Total = rs["num"].Int() - out.CurrentPage = in.PageNum - if in.PageNum*in.PageSize > 100 { - return - } - - // 获取节点字段 - var fileds []string - nodeList, err := service.DataNode().List(ctx, ds.SourceId) - if err != nil { - return - } - for _, v := range nodeList { - fileds = append(fileds, v.Value) - } - - sql = fmt.Sprintf("select %s from %s where device='%s' %s order by ts desc limit %d, %d", strings.Join(fileds, ","), conf.ProductKey, conf.DeviceKey, where, (in.PageNum-1)*in.PageSize, in.PageSize) - data, err := service.TdEngine().GetAll(ctx, sql) - if err != nil { - return - } - - var nr gdb.Result - r := make(gdb.Record, len(nodeList)) - for _, row := range data { - for _, v := range nodeList { - r[v.Key] = row[v.Value] - } - nr = append(nr, r) - } - - out.List = nr.Json() - - return -} - -// 设备源数据记录列表,非分页 -func (s *sDataSource) getDeviceDataAllRecord(ctx context.Context, in *model.SourceDataAllInput, ds *model.DataSourceOutput) (out *model.SourceDataAllOutput, err error) { - out = new(model.SourceDataAllOutput) - - // 设备配置 - conf := ds.DeviceConfig - - // 搜索条件 - var exp []string - if in.Param != nil { - for k, v := range in.Param { - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - tmp := k - for i := 0; i < s.Len(); i++ { - s := fmt.Sprintf("'%v'", v) - tmp = strings.Replace(tmp, "?", s, 1) - } - exp = append(exp, tmp) - } else { - s := fmt.Sprintf("'%v'", v) - exp = append(exp, strings.Replace(k, "?", s, 1)) - } - } - } - where := "" - if len(exp) > 0 { - where = " and " + strings.Join(exp, " and ") - } - - // 获取节点字段 - var fileds []string - nodeList, err := service.DataNode().List(ctx, ds.SourceId) - if err != nil { - return - } - for _, v := range nodeList { - fileds = append(fileds, v.Value) - } - - // TDengine - sql := fmt.Sprintf("select %s from %s where device='%s' %s order by ts desc", strings.Join(fileds, ","), conf.ProductKey, conf.DeviceKey, where) - data, err := service.TdEngine().GetAll(ctx, sql) - if err != nil { - return - } - out.List = data.Json() - - return -} - -// 复制设备数据源 -func (s *sDataSource) copeDeviceSource(ctx context.Context, ds *model.DataSourceOutput) (newSourcId uint64, err error) { - err = dao.DataSource.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 复制源 - in := new(model.DataSourceDeviceAddInput) - in.DataSource = model.DataSource{} - in.Config = model.DataSourceConfigDevice{} - - err = gconv.Scan(ds.DataSource, &in.DataSource) - if err != nil { - return err - } - in.DataSource.Key += "_" + gtime.Now().Format("YmdHis") - - err = gconv.Scan(ds.DeviceConfig, &in.Config) - if err != nil { - return err - } - - newSourcId, err = s.AddDevice(ctx, in) - if err != nil { - return err - } - - // 复制节点 - err = s.copeNode(ctx, ds.SourceId, newSourcId) - - return err - }) - - return -} diff --git a/internal/logic/datahub/data_source_record.go b/internal/logic/datahub/data_source_record.go deleted file mode 100644 index cfe55b3..0000000 --- a/internal/logic/datahub/data_source_record.go +++ /dev/null @@ -1,115 +0,0 @@ -package datahub - -import ( - "context" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -type sDataSourceRecord struct{} - -func init() { - service.RegisterDataSourceRecord(dataSourceRecordNew()) -} - -func dataSourceRecordNew() *sDataSourceRecord { - return &sDataSourceRecord{} -} - -func (s *sDataSourceRecord) GetForTpl(ctx context.Context, sourceId uint64, tid uint64) (rs gdb.Result, err error) { - ds, err := service.DataSource().Detail(ctx, sourceId) - if err != nil { - return - } - if ds == nil { - err = gerror.New("数据源不存在") - return - } - - dsNodes, err := service.DataNode().List(ctx, sourceId) - if err != nil { - return - } - if len(dsNodes) == 0 { - err = gerror.New("数据源未创建节点") - return - } - - dt, err := service.DataTemplate().Detail(ctx, tid) - if err != nil { - return - } - if dt == nil { - err = gerror.New("数据模型不存在") - return - } - - tplNodes, err := service.DataTemplateNode().List(ctx, tid) - if err != nil { - return - } - if len(tplNodes) == 0 { - err = gerror.New("数据模型未创建模型节点") - return - } - - switch ds.From { - case model.DataSourceFromApi: - rs, err = s.getFromApi(ctx, ds, dsNodes, dt, tplNodes) - case model.DataSourceFromDb: - rs, err = s.getFromDb(ctx, ds, dsNodes, dt, tplNodes) - case model.DataSourceFromDevice: - rs, err = s.getFromDevice(ctx, ds, dsNodes, dt, tplNodes) - } - - return -} - -func (s *sDataSourceRecord) getFromDevice(ctx context.Context, ds *model.DataSourceOutput, dsNodes []*model.DataNodeOutput, dt *model.DataTemplateOutput, tplNodes []*model.DataTemplateNodeOutput) (rs gdb.Result, err error) { - table := ds.DeviceConfig.ProductKey - where := "where device='" + ds.DeviceConfig.DeviceKey + "'" - - // TDengine - sql := fmt.Sprintf("select * from %s %s order by ts desc limit 1", table, where) - data, err := service.TdEngine().GetAll(ctx, sql) - if err != nil || data.Len() == 0 { - return - } - - r := make(gdb.Record, len(dsNodes)) - for _, v := range dsNodes { - r[v.Key] = data[0][v.Value] - } - rs = append(rs, r) - - return -} - -func (s *sDataSourceRecord) getFromApi(ctx context.Context, ds *model.DataSourceOutput, dsNodes []*model.DataNodeOutput, dt *model.DataTemplateOutput, tplNodes []*model.DataTemplateNodeOutput) (rs gdb.Result, err error) { - table := getTableName(ds.SourceId) - - groupNum := len(ds.ApiConfig.RequestParams) - if groupNum > 1 { - // 多组数据 - sql := fmt.Sprintf("select * from %s order by created_at desc limit %d", table, groupNum) - rs, err = g.DB(DataCenter()).GetAll(ctx, sql) - return - } - - sql := fmt.Sprintf("select * from %s order by created_at desc limit 1", table) - rs, err = g.DB(DataCenter()).GetAll(ctx, sql) - return -} - -func (s *sDataSourceRecord) getFromDb(ctx context.Context, ds *model.DataSourceOutput, dsNodes []*model.DataNodeOutput, dt *model.DataTemplateOutput, tplNodes []*model.DataTemplateNodeOutput) (rs gdb.Result, err error) { - table := getTableName(ds.SourceId) - - sql := fmt.Sprintf("select * from %s order by created_at desc limit %d", table, ds.DbConfig.Num) - rs, err = g.DB(DataCenter()).GetAll(ctx, sql) - return -} diff --git a/internal/logic/datahub/data_source_record_test.go b/internal/logic/datahub/data_source_record_test.go deleted file mode 100644 index bb81d84..0000000 --- a/internal/logic/datahub/data_source_record_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package datahub - -import ( - "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/tdengine" - "github.com/sagoo-cloud/sagooiot/internal/service" - "testing" - - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" - _ "github.com/taosdata/driver-go/v3/taosRestful" -) - -func TestGetForTpl(t *testing.T) { - out, err := service.DataSourceRecord().GetForTpl(context.TODO(), 43, 9) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} diff --git a/internal/logic/datahub/data_source_test.go b/internal/logic/datahub/data_source_test.go deleted file mode 100644 index d26a425..0000000 --- a/internal/logic/datahub/data_source_test.go +++ /dev/null @@ -1,49 +0,0 @@ -package datahub - -import ( - "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/tdengine" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "testing" - - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" - _ "github.com/taosdata/driver-go/v3/taosRestful" -) - -func TestAllSource(t *testing.T) { - out, err := service.DataSource().AllSource(context.TODO()) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestGetAllDataS(t *testing.T) { - in := &model.SourceDataAllInput{ - SourceId: 45, - Param: map[string]interface{}{ - "pr1=?": "aaa", - }, - } - out, err := service.DataSource().GetAllData(context.TODO(), in) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestUpdateDataS(t *testing.T) { - err := service.DataSource().UpdateData(context.TODO(), 84) - if err != nil { - t.Fatal(err) - } -} - -func TestGetDbData(t *testing.T) { - out, err := service.DataSource().GetDbData(context.TODO(), 77) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} diff --git a/internal/logic/datahub/data_table.go b/internal/logic/datahub/data_table.go deleted file mode 100644 index 0f0e574..0000000 --- a/internal/logic/datahub/data_table.go +++ /dev/null @@ -1,180 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "strconv" - "strings" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -// 数据表名称 -func getTableName(sourceId uint64) string { - return "data_source_" + strconv.FormatUint(sourceId, 10) -} - -// 生成数据表结构 -func createTable(ctx context.Context, sourceId uint64) (table string, err error) { - table = getTableName(sourceId) - - isExsit := checkTableExsit(ctx, table) - if isExsit { - err = gerror.New("数据表" + table + "已存在") - return - } - - // 获取节点 - nodeList, err := service.DataNode().List(ctx, sourceId) - if err != nil { - return - } - if len(nodeList) == 0 { - err = gerror.New("该数据源还未创建数据节点") - return - } - - pk := "" - columns := make([]string, len(nodeList)+1) - for i, v := range nodeList { - switch v.DataType { - case "int": - columns[i] = "`" + v.Key + "` int(11) DEFAULT 0 COMMENT '" + v.Name + "'" - case "long": - columns[i] = "`" + v.Key + "` bigint(20) DEFAULT 0 COMMENT '" + v.Name + "'" - case "float": - columns[i] = "`" + v.Key + "` float DEFAULT 0 COMMENT '" + v.Name + "'" - case "double": - columns[i] = "`" + v.Key + "` double DEFAULT 0 COMMENT '" + v.Name + "'" - case "string": - columns[i] = "`" + v.Key + "` varchar(255) DEFAULT '' COMMENT '" + v.Name + "'" - case "boolean": - columns[i] = "`" + v.Key + "` tinyint DEFAULT 0 COMMENT '" + v.Name + "'" - case "date": - columns[i] = "`" + v.Key + "` datetime DEFAULT NULL COMMENT '" + v.Name + "'" - default: - columns[i] = "`" + v.Key + "` varchar(255) DEFAULT '' COMMENT '" + v.Name + "'" - } - - // 主键 - if v.IsPk == 1 { - pk = ", primary key (`" + v.Key + "`)" - } - } - columns[len(nodeList)] = "`created_at` datetime DEFAULT NULL COMMENT '创建时间'" - - sql := "CREATE TABLE " + table + " ( " + strings.Join(columns, ",") + pk + " );" - _, err = g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return - } - - return -} - -// 表删除 -func dropTable(ctx context.Context, sourceId uint64) error { - table := getTableName(sourceId) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return nil - } - - sql := "DROP TABLE " + table - _, err := g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return err - } - return nil -} - -// 字段增加 -func addColumn(ctx context.Context, nodeId uint64) (err error) { - var p *entity.DataNode - err = dao.DataNode.Ctx(ctx).Where(dao.DataNode.Columns().NodeId, nodeId).Scan(&p) - if err != nil { - return - } - if p == nil { - return gerror.New("数据节点不存在") - } - - table := getTableName(p.SourceId) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return gerror.New("数据表" + table + "不存在") - } - - column := "" - - switch p.DataType { - case "int": - column = "`" + p.Key + "` int(11) DEFAULT 0 COMMENT '" + p.Name + "'" - case "long": - column = "`" + p.Key + "` bigint(20) DEFAULT 0 COMMENT '" + p.Name + "'" - case "float": - column = "`" + p.Key + "` float DEFAULT 0 COMMENT '" + p.Name + "'" - case "double": - column = "`" + p.Key + "` double DEFAULT 0 COMMENT '" + p.Name + "'" - case "string": - column = "`" + p.Key + "` varchar(255) DEFAULT '' COMMENT '" + p.Name + "'" - case "boolean": - column = "`" + p.Key + "` tinyint DEFAULT 0 COMMENT '" + p.Name + "'" - case "date": - column = "`" + p.Key + "` datetime DEFAULT NULL COMMENT '" + p.Name + "'" - default: - column = "`" + p.Key + "` varchar(255) DEFAULT '' COMMENT '" + p.Name + "'" - } - - sql := "ALTER TABLE " + table + " ADD COLUMN " + column - _, err = g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return err - } - return nil -} - -// 字段删除 -func dropColumn(ctx context.Context, nodeId uint64) (err error) { - var p *entity.DataNode - err = dao.DataNode.Ctx(ctx).Where(dao.DataNode.Columns().NodeId, nodeId).Scan(&p) - if err != nil { - return - } - if p == nil { - return gerror.New("数据节点不存在") - } - - table := getTableName(p.SourceId) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return gerror.New("数据表" + table + "不存在") - } - - sql := "ALTER TABLE " + table + " DROP `" + p.Key + "`" - _, err = g.DB(DataCenter()).Exec(ctx, sql) - - return err -} - -// 检查表是否存在, true:已存在,false:不存在 -func checkTableExsit(ctx context.Context, table string) bool { - sql := "select * from information_schema.tables where table_name = ? and table_schema = (select database()) limit 1" - one, _ := g.DB(DataCenter()).GetOne(ctx, sql, table) - return one != nil -} - -// 获取数据库分组 -func DataCenter() string { - name, _ := g.Cfg().Get(context.TODO(), "database.dataCenter.link") - if name.String() != "" { - return "dataCenter" - } - return "" -} diff --git a/internal/logic/datahub/data_table_test.go b/internal/logic/datahub/data_table_test.go deleted file mode 100644 index 3fe9adf..0000000 --- a/internal/logic/datahub/data_table_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package datahub - -import ( - "context" - "testing" - - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" -) - -func TestCreateTable(t *testing.T) { - table, err := createTable(context.TODO(), 1) - if err != nil { - t.Fatal(err) - } - t.Log(table) -} diff --git a/internal/logic/datahub/data_template.go b/internal/logic/datahub/data_template.go deleted file mode 100644 index 56c931c..0000000 --- a/internal/logic/datahub/data_template.go +++ /dev/null @@ -1,727 +0,0 @@ -package datahub - -import ( - "context" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "reflect" - "strings" - - "github.com/go-gota/gota/dataframe" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -type sDataTemplate struct{} - -func init() { - service.RegisterDataTemplate(dataTemplateNew()) -} - -func dataTemplateNew() *sDataTemplate { - return &sDataTemplate{} -} - -func (s *sDataTemplate) Add(ctx context.Context, in *model.DataTemplateAddInput) (id uint64, err error) { - tid, _ := dao.DataTemplate.Ctx(ctx). - Fields(dao.DataTemplate.Columns().Id). - Where(dao.DataTemplate.Columns().Key, in.Key). - Value() - if tid.Int64() > 0 { - err = gerror.New("数据模型标识重复") - return - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataTemplate - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - param.Status = 0 - param.LockKey = 0 - - rs, err := dao.DataTemplate.Ctx(ctx).Data(param).Insert() - if err != nil { - return - } - - newId, _ := rs.LastInsertId() - id = uint64(newId) - - return -} - -func (s *sDataTemplate) Edit(ctx context.Context, in *model.DataTemplateEditInput) (err error) { - out, err := s.Detail(ctx, in.Id) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据模型不存在") - } - if out.Status == model.DataTemplateStatusOn { - return gerror.New("数据模型已发布,请先撤回") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataTemplate - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.UpdateBy = uint(loginUserId) - param.Id = nil - if out.LockKey == 1 { - param.Key = nil - } else { - id, _ := dao.DataTemplate.Ctx(ctx). - Fields(dao.DataTemplate.Columns().Id). - Where(dao.DataTemplate.Columns().Key, in.Key). - WhereNot(dao.DataTemplate.Columns().Id, in.Id). - Value() - if id.Int64() > 0 { - err = gerror.New("数据模型标识重复") - return - } - } - - err = dao.DataTemplate.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = dao.DataTemplate.Ctx(ctx). - Data(param). - Where(dao.DataTemplate.Columns().Id, in.Id). - Update() - if err != nil { - return err - } - - // 同步聚合时间到定时任务管理 - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataTemplate-" + gconv.String(out.Id) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) > 0 { - editJob := new(model.SysJobEditInput) - editJob.JobName = list[0].JobName - editJob.JobParams = list[0].JobParams - editJob.JobGroup = list[0].JobGroup - editJob.InvokeTarget = list[0].InvokeTarget - editJob.CronExpression = in.CronExpression - editJob.MisfirePolicy = list[0].MisfirePolicy - editJob.Concurrent = list[0].Concurrent - editJob.Status = list[0].Status - editJob.Remark = list[0].Remark - editJob.JobId = list[0].JobId - editJob.UpdateBy = uint64(loginUserId) - err = service.SysJob().EditJob(ctx, editJob) - if err != nil { - return err - } - } - - // 绑定业务 - err = service.DataTemplateBusi().Add(ctx, &model.DataTemplateBusiAddInput{ - DataTemplateId: in.Id, - BusiTypes: in.BusiTypes, - }) - - return err - }) - - return -} - -func (s *sDataTemplate) Del(ctx context.Context, ids []uint64) (err error) { - var p []*entity.DataTemplate - err = dao.DataTemplate.Ctx(ctx).WhereIn(dao.DataTemplate.Columns().Id, ids).Scan(&p) - if len(p) == 0 { - return gerror.New("数据模型不存在") - } - if len(p) == 1 && p[0].Status == model.DataTemplateStatusOn { - return gerror.New("数据模型已发布,请先撤回,再删除") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataTemplate.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 删除数据模型 - var delIds []uint64 - for _, id := range ids { - rs, err := dao.DataTemplate.Ctx(ctx). - Data(do.DataTemplate{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataTemplate.Columns().Id, id). - Where(dao.DataTemplate.Columns().Status, model.DataTemplateStatusOff). - Unscoped(). - Update() - if err != nil { - return err - } - - num, _ := rs.RowsAffected() - if num > 0 { - delIds = append(delIds, id) - } - } - - // 删除数据模型节点 - for _, id := range delIds { - _, err = dao.DataTemplateNode.Ctx(ctx). - Data(do.DataTemplateNode{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataTemplateNode.Columns().Tid, id). - Unscoped(). - Update() - if err != nil { - return err - } - } - - // 删除定时任务、删除数据表 - for _, id := range delIds { - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataTemplate-" + gconv.String(id) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) != 0 { - err = service.SysJob().DeleteJobByIds(ctx, []int{int(list[0].JobId)}) - } - - if err = dropTplTable(ctx, id); err != nil { - return err - } - } - - // 绑定业务 - for _, id := range delIds { - err = service.DataTemplateBusi().Del(ctx, id) - if err != nil { - return err - } - } - return nil - }) - - return -} - -func (s *sDataTemplate) Search(ctx context.Context, in *model.DataTemplateSearchInput) (out *model.DataTemplateSearchOutput, err error) { - out = new(model.DataTemplateSearchOutput) - c := dao.DataTemplate.Columns() - m := dao.DataTemplate.Ctx(ctx).WithAll().OrderDesc(c.Id) - - if in.Key != "" { - m = m.Where(c.Key, in.Key) - } - if in.Name != "" { - m = m.WhereLike(c.Name, "%"+in.Name+"%") - } - - out.Total, _ = m.Count() - out.CurrentPage = in.PageNum - if err = m.Page(in.PageNum, in.PageSize).Scan(&out.List); err != nil { - return - } - - for k, v := range out.List { - if len(v.DataTemplateBusi) > 0 { - for _, bs := range v.DataTemplateBusi { - out.List[k].BusiTypes = append(out.List[k].BusiTypes, bs.BusiTypes) - } - } - } - - return -} - -func (s *sDataTemplate) List(ctx context.Context) (list []*entity.DataTemplate, err error) { - c := dao.DataTemplate.Columns() - err = dao.DataTemplate.Ctx(ctx).Where(c.Status, model.DataTemplateStatusOn).OrderDesc(c.Id).Scan(&list) - return -} - -func (s *sDataTemplate) Detail(ctx context.Context, id uint64) (out *model.DataTemplateOutput, err error) { - err = dao.DataTemplate.Ctx(ctx).Where(dao.DataTemplate.Columns().Id, id).Scan(&out) - return -} - -func (s *sDataTemplate) Deploy(ctx context.Context, id uint64) (err error) { - out, err := s.Detail(ctx, id) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据模型不存在") - } - if out.Status == model.DataTemplateStatusOn { - return gerror.New("数据模型已发布") - } - - // 获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataTemplate.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 创建表结构 - table := "" - if out.DataTable == "" { - table, err = createTplTable(ctx, id) - if err != nil { - return err - } - } - - m := g.Map{ - dao.DataTemplate.Columns().Status: model.DataTemplateStatusOn, - dao.DataTemplate.Columns().LockKey: 1, - } - if table != "" { - m[dao.DataTemplate.Columns().DataTable] = table - } - - _, err = dao.DataTemplate.Ctx(ctx). - Data(m). - Where(dao.DataTemplate.Columns().Id, id). - Update() - - return err - }) - if err != nil { - return err - } - - // 开启数据更新任务,任务排重 -Job: - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataTemplate-" + gconv.String(id) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) == 0 { - sysJob := new(model.SysJobAddInput) - sysJob.JobName = "dataTemplate-" + gconv.String(id) - sysJob.JobParams = gconv.String(id) - sysJob.JobGroup = "dataSourceJob" - sysJob.InvokeTarget = "dataTemplate" - sysJob.CronExpression = out.CronExpression - sysJob.MisfirePolicy = 1 - sysJob.Concurrent = 0 - sysJob.Status = 0 - sysJob.Remark = out.Name - sysJob.CreateBy = uint64(loginUserId) - err = service.SysJob().AddJob(ctx, sysJob) - goto Job - } - err = service.SysJob().JobStart(ctx, list[0]) - // 初始执行一次 - err = service.SysJob().JobRun(ctx, list[0]) - - return -} - -func (s *sDataTemplate) Undeploy(ctx context.Context, id uint64) (err error) { - out, err := s.Detail(ctx, id) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据模型不存在") - } - if out.Status == model.DataTemplateStatusOff { - return gerror.New("数据模型已停用") - } - - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{dao.DataTemplate.Columns().Status: model.DataTemplateStatusOff}). - Where(dao.DataTemplate.Columns().Id, id). - Update() - if err != nil { - return err - } - - // 关闭数据更新任务 - job := new(model.GetJobListInput) - job.JobGroup = "dataSourceJob" - job.JobName = "dataTemplate-" + gconv.String(id) - job.PaginationInput = &model.PaginationInput{PageNum: 1, PageSize: 1} - _, list, _ := service.SysJob().JobList(ctx, job) - if len(list) == 0 { - return - } - err = service.SysJob().JobStop(ctx, list[0]) - - return -} - -// GetData 获取数据模型的聚合数据 -func (s *sDataTemplate) GetData(ctx context.Context, in *model.DataTemplateDataInput) (out *model.DataTemplateDataOutput, err error) { - dt, err := s.Detail(ctx, in.Id) - if err != nil { - return - } - if dt == nil { - err = gerror.New("数据模型不存在") - return - } - - sortNode := "created_at desc" - if dt.SortNodeKey != "" { - sortDesc := "desc" - if dt.SortDesc == 2 { - sortDesc = "asc" - } - sortNode = dt.SortNodeKey + " " + sortDesc - } - - out = new(model.DataTemplateDataOutput) - - table := getTplTableName(in.Id) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " where " + strings.Join(exp, " and ") - } - - sql := "select * from " + table + where + " order by " + sortNode - - out.Total, _ = g.DB(DataCenter()).GetCount(ctx, sql, value...) - out.CurrentPage = in.PageNum - - sql += fmt.Sprintf(" limit %d, %d", (in.PageNum-1)*in.PageSize, in.PageSize) - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - out.List = rs.Json() - - return -} - -// 获取数据模型的聚合数据,不分页 -func (s *sDataTemplate) GetAllData(ctx context.Context, in *model.TemplateDataAllInput) (out *model.TemplateDataAllOutput, err error) { - dt, err := s.Detail(ctx, in.Id) - if err != nil { - return - } - if dt == nil { - err = gerror.New("数据模型不存在") - return - } - - sortNode := "CREATED_AT ASC" - if dt.SortNodeKey != "" { - sortDesc := "DESC" - if dt.SortDesc == 2 { - sortDesc = "ASC" - } - sortNode = dt.SortNodeKey + " " + sortDesc - } - - out = new(model.TemplateDataAllOutput) - - table := getTplTableName(in.Id) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " WHERE " + strings.Join(exp, " AND ") - } - - sql := "SELECT * FROM " + table + where + " ORDER BY " + sortNode - rs, err := g.DB(DataCenter()).GetAll(ctx, sql, value...) - if err != nil { - return - } - err = gconv.Scan(rs.List(), &out.List) - - return -} - -// 获取数据,返回dataframe -func (s *sDataTemplate) GetDataBySql(ctx context.Context, sql string) (df dataframe.DataFrame, err error) { - rs, err := g.DB(DataCenter()).GetAll(ctx, sql) - if err != nil { - return - } - df = dataframe.LoadMaps(rs.List()) - return -} - -// GetDataByTableName 获取数据,返回dataframe -func (s *sDataTemplate) GetDataByTableName(ctx context.Context, tableName string) (df dataframe.DataFrame, err error) { - rs, err := g.DB(DataCenter()).Model(tableName).All() - if err != nil { - return - } - df = dataframe.LoadMaps(rs.List()) - return -} - -// 获取最后一条记录 -func (s *sDataTemplate) GetLastData(ctx context.Context, in *model.TemplateDataLastInput) (out *model.TemplateDataLastOutput, err error) { - dt, err := s.Detail(ctx, in.Id) - if err != nil { - return - } - if dt == nil { - err = gerror.New("数据模型不存在") - return - } - - sortNode := "CREATED_AT DESC" - - out = new(model.TemplateDataLastOutput) - - table := getTplTableName(in.Id) - - // 搜索条件 - var exp []string - var value []any - if in.Param != nil { - for k, v := range in.Param { - exp = append(exp, k) - if reflect.TypeOf(v).Kind() == reflect.Slice { - s := reflect.ValueOf(v) - for i := 0; i < s.Len(); i++ { - ele := s.Index(i) - value = append(value, ele.Interface()) - } - } else { - value = append(value, v) - } - } - } - where := "" - if len(exp) > 0 { - where = " WHERE " + strings.Join(exp, " AND ") - } - - sql := "SELECT * FROM " + table + where + " ORDER BY " + sortNode + " LIMIT 1" - rs, err := g.DB(DataCenter()).GetOne(ctx, sql, value...) - if err != nil { - return - } - err = gconv.Scan(rs, &out.Data) - - return -} - -// 更新数据记录,定时任务触发 -func (s *sDataTemplate) UpdateData(ctx context.Context, id uint64) error { - err := service.DataTemplateRecord().UpdateData(ctx, id) - return err -} - -func (s *sDataTemplate) GetInfoByIds(ctx context.Context, ids []uint64) (data []*entity.DataTemplate, err error) { - var p []*entity.DataTemplate - err = dao.DataTemplate.Ctx(ctx).WhereIn(dao.DataTemplate.Columns().Id, ids).Where(g.Map{ - dao.DataTemplate.Columns().Status: 1, - }).Scan(&p) - if err != nil { - return - } - if p == nil { - return - } - return p, err -} - -// 数据模型获取数据的内网方法列表,供大屏使用 -func (s *sDataTemplate) AllTemplate(ctx context.Context) (out []*model.AllTemplateOut, err error) { - err = dao.DataTemplate.Ctx(ctx). - Where(dao.DataTemplate.Columns().Status, model.DataTemplateStatusOn). - OrderDesc(dao.DataTemplate.Columns().Id). - Scan(&out) - if err != nil { - return - } - - for _, v := range out { - v.Path = "GetAllData?pageNum=1&pageSize=10&id=" + gconv.String(v.Id) - } - - return -} - -// 更新数据聚合时长 -func (s *sDataTemplate) UpdateInterval(ctx context.Context, id uint64, cronExpression string) (err error) { - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{ - dao.DataTemplate.Columns().CronExpression: cronExpression, - }). - Where(dao.DataTemplate.Columns().Id, id). - Update() - - return -} - -// 复制数据模型 -func (s *sDataTemplate) CopeTemplate(ctx context.Context, id uint64) (err error) { - out, err := s.Detail(ctx, id) - if err != nil { - return err - } - if out == nil { - return gerror.New("数据模型不存在") - } - - err = dao.DataTemplate.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - // 复制模型 - in := new(model.DataTemplateAddInput) - - err = gconv.Scan(out, &in) - if err != nil { - return err - } - in.Key += "_" + gtime.Now().Format("YmdHis") - - newId, err := s.Add(ctx, in) - if err != nil { - return err - } - - // 复制节点 - tplNodes, err := service.DataTemplateNode().List(ctx, id) - if err != nil || len(tplNodes) == 0 { - return err - } - - for _, v := range tplNodes { - var in *model.DataTemplateNodeAddInput - err = gconv.Scan(v.DataTemplateNode, &in) - if err != nil { - return err - } - in.Tid = newId - in.IsSorting = v.IsSorting - in.IsDesc = v.IsDesc - - err = service.DataTemplateNode().Add(ctx, in) - if err != nil { - return err - } - } - - return nil - }) - - return -} - -// 检测数据模型是否需要设置关联 -func (s *sDataTemplate) CheckRelation(ctx context.Context, id uint64) (yes bool, err error) { - rs, err := dao.DataTemplateNode.Ctx(ctx).Fields("count(distinct source_id)").Where(dao.DataTemplateNode.Columns().Tid, id).Value() - if err != nil || rs.Int() <= 1 { - return - } - - dt, err := s.Detail(ctx, id) - if err != nil { - return - } - if dt == nil { - err = gerror.New("数据模型不存在") - return - } - - if dt.MainSourceId == 0 || dt.SourceNodeKey == "" { - yes = true - } - - return -} - -// 设置主源、关联字段 -func (s *sDataTemplate) SetRelation(ctx context.Context, in *model.TemplateDataRelationInput) (err error) { - dt, err := s.Detail(ctx, in.Id) - if err != nil { - return - } - if dt == nil { - return gerror.New("数据模型不存在") - } - if dt.Status == model.DataTemplateStatusOn { - return gerror.New("数据模型已发布,请先撤回") - } - - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{ - dao.DataTemplate.Columns().MainSourceId: in.MainSourceId, - dao.DataTemplate.Columns().SourceNodeKey: in.SourceNodeKey, - }). - Where(dao.DataTemplate.Columns().Id, in.Id). - Update() - return -} - -// 数据源列表 -func (s *sDataTemplate) SourceList(ctx context.Context, id uint64) (list []*model.DataSourceOutput, err error) { - rs, err := dao.DataTemplateNode.Ctx(ctx).Fields("source_id"). - Where(dao.DataTemplateNode.Columns().Tid, id). - Group(dao.DataTemplateNode.Columns().SourceId). - Array() - if err != nil || len(rs) == 0 { - return - } - - for _, v := range rs { - ds, err := service.DataSource().Detail(ctx, v.Uint64()) - if err != nil { - return nil, err - } - list = append(list, ds) - } - - return -} diff --git a/internal/logic/datahub/data_template_busi.go b/internal/logic/datahub/data_template_busi.go deleted file mode 100644 index c23366a..0000000 --- a/internal/logic/datahub/data_template_busi.go +++ /dev/null @@ -1,113 +0,0 @@ -package datahub - -import ( - "context" - "fmt" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "strconv" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -type sDataTemplateBusi struct{} - -func init() { - service.RegisterDataTemplateBusi(dataTemplateBusiNew()) -} - -func dataTemplateBusiNew() *sDataTemplateBusi { - return &sDataTemplateBusi{} -} - -func (s *sDataTemplateBusi) Add(ctx context.Context, in *model.DataTemplateBusiAddInput) (err error) { - if len(in.BusiTypes) == 0 { - return - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - // 获取业务单元类型 - var dtList []model.SysDictDataOut - dao.SysDictData.Ctx(ctx).Where(dao.SysDictType.Columns().DictType, "busi_types").Scan(&dtList) - - c := dao.DataTemplateBusi.Columns() - for _, v := range in.BusiTypes { - dtb, _ := dao.DataTemplateBusi.Ctx(ctx). - Where(c.BusiTypes, v). - One() - if len(dtb) > 0 { - if dtb["data_template_id"].Uint64() == in.DataTemplateId { - continue - } - - var name string - for _, d := range dtList { - if d.DictValue == strconv.Itoa(v) { - name = d.DictLabel - } - } - err = gerror.Newf("%s, 该业务已绑定其他模型", name) - return - } - - param := do.DataTemplateBusi{ - DataTemplateId: in.DataTemplateId, - BusiTypes: v, - CreatedBy: uint(loginUserId), - } - _, err = dao.DataTemplateBusi.Ctx(ctx).Data(param).Insert() - } - - return -} - -func (s *sDataTemplateBusi) GetInfos(ctx context.Context, busiTypes int) (data *entity.DataTemplateBusi, err error) { - err = dao.DataTemplateBusi.Ctx(ctx).Where(g.Map{ - dao.DataTemplateBusi.Columns().BusiTypes: busiTypes, - dao.DataTemplateBusi.Columns().IsDeleted: 0, - }).Scan(&data) - return -} - -func (s *sDataTemplateBusi) GetInfo(ctx context.Context, busiTypes int) (data *entity.DataTemplateBusi, err error) { - err = dao.DataTemplateBusi.Ctx(ctx).Where(g.Map{ - dao.DataTemplateBusi.Columns().BusiTypes: busiTypes, - dao.DataTemplateBusi.Columns().IsDeleted: 0, - }).Scan(&data) - return -} - -func (s *sDataTemplateBusi) GetTable(ctx context.Context, busiTypes int) (table string, err error) { - busi, err := s.GetInfos(ctx, busiTypes) - if err != nil { - return - } - if busi == nil { - err = gerror.New("未绑定数据模型") - return - } - table = fmt.Sprintf("data_template_%d", busi.DataTemplateId) - return -} - -func (s *sDataTemplateBusi) Del(ctx context.Context, tid uint64) error { - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - _, err := dao.DataTemplateBusi.Ctx(ctx). - Data(do.DataTemplateBusi{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataTemplateBusi.Columns().DataTemplateId, tid). - Unscoped(). - Update() - return err -} diff --git a/internal/logic/datahub/data_template_node.go b/internal/logic/datahub/data_template_node.go deleted file mode 100644 index 68c1995..0000000 --- a/internal/logic/datahub/data_template_node.go +++ /dev/null @@ -1,209 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -type sDataTemplateNode struct{} - -func init() { - service.RegisterDataTemplateNode(dataTemplateNodeNew()) -} - -func dataTemplateNodeNew() *sDataTemplateNode { - return &sDataTemplateNode{} -} - -func (s *sDataTemplateNode) Add(ctx context.Context, in *model.DataTemplateNodeAddInput) (err error) { - id, _ := dao.DataTemplateNode.Ctx(ctx). - Fields(dao.DataTemplateNode.Columns().Id). - Where(dao.DataTemplateNode.Columns().Tid, in.Tid). - Where(dao.DataTemplateNode.Columns().Key, in.Key). - Value() - if id.Int64() > 0 { - return gerror.New("数据模型节点标识重复") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataTemplateNode - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - if in.Method == "" { - param.Method = nil - } - - err = dao.DataTemplateNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - rs, err := dao.DataTemplateNode.Ctx(ctx).Data(param).Insert() - if err != nil { - return err - } - - nodeId, _ := rs.LastInsertId() - - dataTemplate, _ := service.DataTemplate().Detail(ctx, in.Tid) - if dataTemplate != nil && dataTemplate.DataTable != "" { - // 表结构已存在,字段新增 - err = addTplColumn(ctx, uint64(nodeId)) - if err != nil { - return err - } - } - - // 处理排序字段 - if in.IsSorting == 1 { - c := dao.DataTemplate.Columns() - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{c.SortNodeKey: in.Key, c.SortDesc: in.IsDesc}). - Where(c.Id, in.Tid). - Update() - if err != nil { - return err - } - } - - return nil - }) - - return -} - -func (s *sDataTemplateNode) Edit(ctx context.Context, in *model.DataTemplateNodeEditInput) (err error) { - var p *entity.DataTemplateNode - err = dao.DataTemplateNode.Ctx(ctx).Where(dao.DataTemplateNode.Columns().Id, in.Id).Scan(&p) - if p == nil { - return gerror.New("数据模型节点不存在") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - var param *do.DataTemplateNode - err = gconv.Scan(in, ¶m) - param.UpdateBy = uint(loginUserId) - param.Id = nil - if in.SourceId == p.SourceId { - param.SourceId = nil - if in.NodeId == p.NodeId { - param.NodeId = nil - } - } - - err = dao.DataTemplateNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = dao.DataTemplateNode.Ctx(ctx).Data(param).Where(dao.DataTemplateNode.Columns().Id, in.Id).Update() - if err != nil { - return err - } - - // 处理排序字段 - sortNode := "" - sortDesc := 0 - if in.IsSorting == 1 { - sortNode = p.Key - sortDesc = in.IsDesc - } - c := dao.DataTemplate.Columns() - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{c.SortNodeKey: sortNode, c.SortDesc: sortDesc}). - Where(c.Id, p.Tid). - WhereNot(c.SortNodeKey, sortNode). - Update() - return err - }) - - return -} - -func (s *sDataTemplateNode) Del(ctx context.Context, id uint64) (err error) { - var p *entity.DataTemplateNode - err = dao.DataTemplateNode.Ctx(ctx).Where(dao.DataTemplateNode.Columns().Id, id).Scan(&p) - if p == nil { - return gerror.New("数据模型节点不存在") - } - - var dt *entity.DataTemplate - err = dao.DataTemplate.Ctx(ctx).Where(dao.DataTemplate.Columns().Id, p.Tid).Scan(&dt) - if err != nil { - return - } - if dt != nil && dt.Status != model.DataTemplateStatusOff { - return gerror.New("数据模型已发布,请先撤回,再删除") - } - - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - - err = dao.DataTemplateNode.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - if dt != nil && dt.DataTable != "" { - // 表结构已存在,字段须删除处理 - if err = dropTplColumn(ctx, id); err != nil { - return err - } - } - - _, err = dao.DataTemplateNode.Ctx(ctx). - Data(do.DataTemplateNode{ - DeletedBy: uint(loginUserId), - DeletedAt: gtime.Now(), - }). - Where(dao.DataTemplateNode.Columns().Id, id). - Unscoped(). - Update() - if err != nil { - return err - } - - // 处理排序字段 - if dt.SortNodeKey == p.Key { - c := dao.DataTemplate.Columns() - _, err = dao.DataTemplate.Ctx(ctx). - Data(g.Map{c.SortNodeKey: "", c.SortDesc: 0}). - Where(c.Id, p.Tid). - Update() - if err != nil { - return err - } - } - - return nil - }) - - return -} - -func (s *sDataTemplateNode) List(ctx context.Context, tid uint64) (list []*model.DataTemplateNodeOutput, err error) { - err = dao.DataTemplateNode.Ctx(ctx).WithAll().OrderAsc(dao.DataTemplateNode.Columns().Id).Where(dao.DataTemplateNode.Columns().Tid, tid).Scan(&list) - if err != nil || len(list) == 0 { - return - } - - dt, err := service.DataTemplate().Detail(ctx, tid) - if err != nil || dt == nil { - return - } - - for _, v := range list { - if dt.SortNodeKey == v.Key { - v.IsSorting = 1 - v.IsDesc = dt.SortDesc - break - } - } - - return -} diff --git a/internal/logic/datahub/data_template_record.go b/internal/logic/datahub/data_template_record.go deleted file mode 100644 index e2ace02..0000000 --- a/internal/logic/datahub/data_template_record.go +++ /dev/null @@ -1,230 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "strconv" - - "github.com/gogf/gf/v2/container/gmap" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/guid" -) - -type sDataTemplateRecord struct{} - -func init() { - service.RegisterDataTemplateRecord(dataTemplateRecordNew()) -} - -func dataTemplateRecordNew() *sDataTemplateRecord { - return &sDataTemplateRecord{} -} - -// 更新数据记录,定时任务触发 -func (s *sDataTemplateRecord) UpdateData(ctx context.Context, tid uint64) error { - dt, err := service.DataTemplate().Detail(ctx, tid) - if err != nil { - return err - } - if dt == nil { - return gerror.New("数据模型不存在") - } - - // 获取节点 - tplNodes, err := service.DataTemplateNode().List(ctx, tid) - if err != nil { - return err - } - if len(tplNodes) == 0 { - return gerror.New("数据模型未创建模型节点") - } - - // 合并数据 - var insertData []*gmap.AnyAnyMap - if dt.MainSourceId > 0 && dt.SourceNodeKey != "" { - if insertData, err = s.mergeForRelation(ctx, dt, tplNodes); err != nil { - return err - } - } else { - if insertData, err = s.merge(ctx, dt, tplNodes); err != nil { - return err - } - } - - // 入库 - if len(insertData) > 0 { - table := getTplTableName(tid) - if err = g.DB(DataCenter()).GetCore().ClearTableFields(ctx, table); err != nil { - return err - } - for i := 0; i < len(insertData); i = i + 1000 { - s := 1000 + i - if len(insertData)-i < 1000 { - s = len(insertData) - } - d := insertData[i:s] - if _, err = g.DB(DataCenter()).Save(ctx, table, d); err != nil { - return err - } - } - } - - return nil -} - -// 按关联字段合并 -func (s *sDataTemplateRecord) mergeForRelation(ctx context.Context, dt *model.DataTemplateOutput, nodes []*model.DataTemplateNodeOutput) (data []*gmap.AnyAnyMap, err error) { - // 统计数据源 - sourceIds := make(map[uint64]struct{}) - for _, node := range nodes { - if node.SourceId > 0 { - sourceIds[node.SourceId] = struct{}{} - } - } - if len(sourceIds) == 0 { - err = gerror.New("数据模型未关联数据源节点") - return - } - - // 获取数据源数据 - var mainSource map[string]*gmap.StrAnyMap - recordData := make(map[uint64]map[string]*gmap.StrAnyMap) - for sid := range sourceIds { - rs, err := s.getSourceRecordForRelation(ctx, sid, dt, nodes) - if err != nil { - return nil, err - } - recordData[sid] = rs - - if sid == dt.MainSourceId || len(sourceIds) == 1 { - mainSource = rs - } - } - - // 合并数据 - for rKey, row := range mainSource { - m := gmap.New() - for _, node := range nodes { - if node.From == 2 { - // 数据源关联节点 - if node.SourceId == dt.MainSourceId { - m.Set(node.Key, row.Get(node.Key)) - } else { - if row, ok := recordData[node.SourceId]; ok { - if rowD, ok := row[rKey]; ok { - m.Set(node.Key, rowD.Get(node.Key)) - } else { - m.Set(node.Key, "") - } - } - } - } else { - // 自定义节点 - if node.Default != "" { - m.Set(node.Key, node.Default) - } else { - m.Set(node.Key, guid.S()) - } - } - } - data = append(data, m) - } - return -} - -func (s *sDataTemplateRecord) getSourceRecordForRelation(ctx context.Context, sourceId uint64, dt *model.DataTemplateOutput, nodes []*model.DataTemplateNodeOutput) (rs map[string]*gmap.StrAnyMap, err error) { - data, err := service.DataSourceRecord().GetForTpl(ctx, sourceId, dt.Id) - if err != nil { - return - } - - rs = make(map[string]*gmap.StrAnyMap, data.Len()) - for i, row := range data { - m := gmap.NewStrAnyMap() - for _, node := range nodes { - if node.From == 2 && node.SourceId == sourceId { - m.Set(node.Key, row[node.Node.Key]) - } - } - - relationKey := row[dt.SourceNodeKey].String() - if relationKey == "" { - relationKey = strconv.Itoa(i) - } - rs[relationKey] = m - } - - return -} - -// 按索引行合并 -func (s *sDataTemplateRecord) merge(ctx context.Context, dt *model.DataTemplateOutput, nodes []*model.DataTemplateNodeOutput) (data []*gmap.AnyAnyMap, err error) { - // 统计数据源 - sourceIds := make(map[uint64]struct{}) - for _, node := range nodes { - if node.SourceId > 0 { - sourceIds[node.SourceId] = struct{}{} - } - } - if len(sourceIds) == 0 { - err = gerror.New("数据模型未关联数据源节点") - return - } - - // 聚合数据源数据 - recordNum := 0 - recordData := make(map[uint64][]*gmap.StrAnyMap) - for sid := range sourceIds { - rs, err := s.getSourceRecord(ctx, sid, dt.Id, nodes) - if err != nil { - return nil, err - } - if recordNum == 0 { - recordNum = len(rs) - } - recordData[sid] = rs - } - - // 合并数据 - for i := 0; i < recordNum; i++ { - m := gmap.New() - for _, node := range nodes { - if node.From == 2 { - // 数据源关联节点 - if row, ok := recordData[node.SourceId]; ok { - m.Set(node.Key, row[i].Get(node.Key)) - } - } else { - // 自定义节点 - if node.Default != "" { - m.Set(node.Key, node.Default) - } else { - m.Set(node.Key, guid.S()) - } - } - } - data = append(data, m) - } - return -} - -func (s *sDataTemplateRecord) getSourceRecord(ctx context.Context, sourceId uint64, tid uint64, nodes []*model.DataTemplateNodeOutput) (rs []*gmap.StrAnyMap, err error) { - data, err := service.DataSourceRecord().GetForTpl(ctx, sourceId, tid) - if err != nil { - return - } - - for _, row := range data { - m := gmap.NewStrAnyMap() - for _, node := range nodes { - if node.From == 2 && node.SourceId == sourceId { - m.Set(node.Key, row[node.Node.Key]) - } - } - rs = append(rs, m) - } - - return -} diff --git a/internal/logic/datahub/data_template_record_test.go b/internal/logic/datahub/data_template_record_test.go deleted file mode 100644 index a9de093..0000000 --- a/internal/logic/datahub/data_template_record_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/service" - "testing" - - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" -) - -func TestUpdateDataRecord(t *testing.T) { - err := service.DataTemplateRecord().UpdateData(context.TODO(), 10) - if err != nil { - t.Fatal(err) - } -} diff --git a/internal/logic/datahub/data_template_table.go b/internal/logic/datahub/data_template_table.go deleted file mode 100644 index 711f98b..0000000 --- a/internal/logic/datahub/data_template_table.go +++ /dev/null @@ -1,181 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "strconv" - "strings" - - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" -) - -// 数据表名称 -func getTplTableName(id uint64) string { - return "data_template_" + strconv.FormatUint(id, 10) -} - -// 生成数据表结构 -func createTplTable(ctx context.Context, id uint64) (table string, err error) { - table = getTplTableName(id) - - isExsit := checkTableExsit(ctx, table) - if isExsit { - err = gerror.New("数据表" + table + "已存在") - return - } - - // 获取节点 - tplNodes, err := service.DataTemplateNode().List(ctx, id) - if err != nil { - return - } - if len(tplNodes) == 0 { - err = gerror.New("该数据模型还未创建模型节点") - return - } - - pk := "" - columns := make([]string, len(tplNodes)+1) - for i, v := range tplNodes { - // 节点默认值,主要处理自动生成的节点 - df := "DEFAULT 0" - if v.DataType == "string" { - df = "DEFAULT ''" - } - if v.From == 1 && v.Default != "" { - df = "DEFAULT '" + v.Default + "'" - } - - switch v.DataType { - case "int": - columns[i] = "`" + v.Key + "` int(11) " + df + " COMMENT '" + v.Name + "'" - case "long": - columns[i] = "`" + v.Key + "` bigint(20) " + df + " COMMENT '" + v.Name + "'" - case "float": - columns[i] = "`" + v.Key + "` float " + df + " COMMENT '" + v.Name + "'" - case "double": - columns[i] = "`" + v.Key + "` double " + df + " COMMENT '" + v.Name + "'" - case "string": - columns[i] = "`" + v.Key + "` varchar(255) " + df + " COMMENT '" + v.Name + "'" - case "boolean": - columns[i] = "`" + v.Key + "` tinyint " + df + " COMMENT '" + v.Name + "'" - case "date": - columns[i] = "`" + v.Key + "` datetime DEFAULT NULL COMMENT '" + v.Name + "'" - default: - columns[i] = "`" + v.Key + "` varchar(255) " + df + " COMMENT '" + v.Name + "'" - } - - // 主键 - if v.IsPk == 1 { - pk = ", primary key (`" + v.Key + "`)" - } - } - columns[len(tplNodes)] = "`created_at` datetime DEFAULT NULL COMMENT '创建时间'" - - sql := "CREATE TABLE " + table + " ( " + strings.Join(columns, ",") + pk + " );" - _, err = g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return - } - - return -} - -// 表删除 -func dropTplTable(ctx context.Context, id uint64) error { - table := getTplTableName(id) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return nil - } - - sql := "DROP TABLE " + table - _, err := g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return err - } - return nil -} - -// 字段增加 -func addTplColumn(ctx context.Context, nodeId uint64) (err error) { - var p *entity.DataTemplateNode - err = dao.DataTemplateNode.Ctx(ctx).Where(dao.DataTemplateNode.Columns().Id, nodeId).Scan(&p) - if err != nil { - return - } - if p == nil { - return gerror.New("模型节点不存在") - } - - table := getTplTableName(p.Tid) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return gerror.New("数据表" + table + "不存在") - } - - // 节点默认值,主要处理自动生成的节点 - df := "DEFAULT 0" - if p.DataType == "string" { - df = "DEFAULT ''" - } - if p.From == 1 && p.Default != "" { - df = "DEFAULT '" + p.Default + "'" - } - - column := "" - switch p.DataType { - case "int": - column = "`" + p.Key + "` int(11) " + df + " COMMENT '" + p.Name + "'" - case "long": - column = "`" + p.Key + "` bigint(20) " + df + " COMMENT '" + p.Name + "'" - case "float": - column = "`" + p.Key + "` float " + df + " COMMENT '" + p.Name + "'" - case "double": - column = "`" + p.Key + "` double " + df + " COMMENT '" + p.Name + "'" - case "string": - column = "`" + p.Key + "` varchar(255) " + df + " COMMENT '" + p.Name + "'" - case "boolean": - column = "`" + p.Key + "` tinyint " + df + " COMMENT '" + p.Name + "'" - case "date": - column = "`" + p.Key + "` datetime DEFAULT NULL COMMENT '" + p.Name + "'" - default: - column = "`" + p.Key + "` varchar(255) " + df + " COMMENT '" + p.Name + "'" - } - - sql := "ALTER TABLE " + table + " ADD COLUMN " + column - _, err = g.DB(DataCenter()).Exec(ctx, sql) - if err != nil { - return err - } - return nil -} - -// 字段删除 -func dropTplColumn(ctx context.Context, nodeId uint64) (err error) { - var p *entity.DataTemplateNode - err = dao.DataTemplateNode.Ctx(ctx).Where(dao.DataTemplateNode.Columns().Id, nodeId).Scan(&p) - if err != nil { - return - } - if p == nil { - return gerror.New("数据节点不存在") - } - - table := getTplTableName(p.Tid) - - isExsit := checkTableExsit(ctx, table) - if !isExsit { - return gerror.New("数据表" + table + "不存在") - } - - sql := "ALTER TABLE " + table + " DROP `" + p.Key + "`" - _, err = g.DB(DataCenter()).Exec(ctx, sql) - - return err -} diff --git a/internal/logic/datahub/data_template_test.go b/internal/logic/datahub/data_template_test.go deleted file mode 100644 index 38525fd..0000000 --- a/internal/logic/datahub/data_template_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package datahub - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "testing" - - _ "github.com/gogf/gf/contrib/drivers/mysql/v2" -) - -func TestAllTemplate(t *testing.T) { - out, err := service.DataTemplate().AllTemplate(context.TODO()) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestGetAllData(t *testing.T) { - in := &model.TemplateDataAllInput{ - Id: 4, - Param: map[string]interface{}{ - "city like ?": "%北京%", - }, - } - out, err := service.DataTemplate().GetAllData(context.TODO(), in) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestGetLastData(t *testing.T) { - in := &model.TemplateDataLastInput{ - Id: 18, - } - out, err := service.DataTemplate().GetLastData(context.TODO(), in) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestUpdateDataTpl(t *testing.T) { - err := service.DataTemplate().UpdateData(context.TODO(), 15) - if err != nil { - t.Fatal(err) - } -} - -func TestGetAllBySql(t *testing.T) { - sql := "select * from data_template_20 order by created_at desc limit 2" - out, err := service.DataTemplate().GetDataBySql(context.TODO(), sql) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestGetAllByTableName(t *testing.T) { - tableName := "data_template_20" - out, err := service.DataTemplate().GetDataByTableName(context.TODO(), tableName) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} - -func TestCheckRelation(t *testing.T) { - out, err := service.DataTemplate().CheckRelation(context.TODO(), 31) - if err != nil { - t.Fatal(err) - } - t.Log(out) -} diff --git a/internal/logic/envirotronics/env_weather.go b/internal/logic/envirotronics/env_weather.go deleted file mode 100644 index 45c5748..0000000 --- a/internal/logic/envirotronics/env_weather.go +++ /dev/null @@ -1,477 +0,0 @@ -package envirotronics - -import ( - "context" - "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/utils" - "regexp" - "strconv" - "strings" - - "github.com/gogf/gf/v2/os/gtime" - "github.com/gogf/gf/v2/util/gconv" -) - -type sEnvWeather struct { -} - -func EnvWeather() *sEnvWeather { - return &sEnvWeather{} -} - -func init() { - service.RegisterEnvWeather(EnvWeather()) -} - -// CityWeatherList 获取城市的风力及日照时长 -func (a *sEnvWeather) CityWeatherList(ctx context.Context) (cityWeatherListOut []*model.CityWeatherListOut, err error) { - info, err := service.CityData().GetAll(ctx) - if err != nil { - return - } - if info != nil { - var cityIds []int - for _, city := range info { - cityIds = append(cityIds, city.Id) - } - if len(cityIds) > 0 { - //绑定的数据建模 - dataInfo, _ := service.DataTemplateBusi().GetInfos(ctx, consts.Weather) - if dataInfo != nil { - //查询数据建模数据 - var in = new(model.TemplateDataAllInput) - in.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - params := map[string]interface{}{ - "CREATED_AT >= ? ": gtime.Now().Format("Y-m-d 00:00:00"), - "CREATED_AT < ? ": gtime.Now().Format("Y-m-d H:i:s"), - } - in.Param = params - out, _ := service.DataTemplate().GetAllData(ctx, in) - - for _, city := range info { - var cityWeather = new(model.CityWeatherListOut) - cityWeather.Id = city.Id - cityWeather.Name = city.Name - cityWeather.Code = city.Code - //获取当前城市当天城市数据 - var cityOut g.List - for _, v := range out.List { - if strings.EqualFold(city.Code, v["adcode"].(string)) { - cityOut = append(cityOut, v) - } - } - if len(cityOut) > 0 { - if err = gconv.Scan(cityOut[len(cityOut)-1], &cityWeather); err != nil { - return - } - } - //计算日照时长 - if cityWeather.Sunrise != "" && cityWeather.Sunset != "" { - cityWeather.SunshineDuration = int(gtime.New(gtime.Now().Format("Y-m-d ") + cityWeather.Sunset + ":00").Sub(gtime.New(gtime.Now().Format("Y-m-d ") + cityWeather.Sunrise + ":00")).Hours()) - } - - cityWeatherListOut = append(cityWeatherListOut, cityWeather) - } - } - } - } - return -} - -// GetCityWeatherById 根据ID获取指定城市的天气 -func (a *sEnvWeather) GetCityWeatherById(ctx context.Context, id int) (cityWeatherListOut *model.CityWeatherListOut, err error) { - info, err := service.CityData().GetInfoById(ctx, id) - if err != nil { - return - } - cityWeatherListOut = new(model.CityWeatherListOut) - if info != nil { - //绑定的数据建模 - dataInfo, _ := service.DataTemplateBusi().GetInfo(ctx, consts.Weather) - if dataInfo != nil { - cityWeatherListOut.Id = info.Id - cityWeatherListOut.Name = info.Name - cityWeatherListOut.Code = info.Code - - var in = new(model.TemplateDataLastInput) - in.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - params := map[string]interface{}{ - "CREATED_AT >= ? ": gtime.Now().Format("Y-m-d 00:00:00"), - "CREATED_AT < ? ": gtime.Now().Format("Y-m-d H:i:s"), - "ADCODE = ? ": info.Code, - } - in.Param = params - //获取数据建模数据 - out, _ := service.DataTemplate().GetLastData(ctx, in) - if out.Data != nil { - if err = gconv.Scan(out.Data, &cityWeatherListOut); err != nil { - return - } - } - - //计算日照时长 - if cityWeatherListOut.Sunrise != "" && cityWeatherListOut.Sunset != "" { - cityWeatherListOut.SunshineDuration = int(gtime.New(gtime.Now().Format("Y-m-d ") + cityWeatherListOut.Sunset + ":00").Sub(gtime.New(gtime.Now().Format("Y-m-d ") + cityWeatherListOut.Sunrise + ":00")).Hours()) - } - } - } - return -} - -// GetCityTemperatureById 根据ID获取指定城市的温度图表 -func (a *sEnvWeather) GetCityTemperatureById(ctx context.Context, id int, types int) (cityWeatherEchartOut []*model.CityWeatherEchartOut, avgCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastAvgCityWeatherEchartOut []*model.CityWeatherEchartOut, err error) { - info, err := service.CityData().GetInfoById(ctx, id) - if err != nil { - return - } - if info != nil { - //绑定的数据建模 - dataInfo, _ := service.DataTemplateBusi().GetInfo(ctx, consts.Weather) - if dataInfo != nil { - //根据类型获取时间差、开始时间、结束时间 - index, begin, end := utils.GetTimeByType(types) - - //封装数据建模 - var in = new(model.TemplateDataAllInput) - //数据建模ID - in.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - params := map[string]interface{}{ - "CREATED_AT >= ? ": begin, - "CREATED_AT < ? ": end, - "ADCODE = ? ": info.Code, - } - in.Param = params - - //获取数据建模数据 - out, _ := service.DataTemplate().GetAllData(ctx, in) - - var sumValue float64 - var foreCastSumValue float64 - for i := 0; i < index; i++ { - //实时天气 - var cityWeatherEchart = new(model.CityWeatherEchartOut) - startTime, endTime, duration, unit := utils.GetTime(i, types, begin) - cityWeatherEchart.Time = strconv.Itoa(duration) + unit - var value float64 - var num float64 - for _, m := range out.List { - if gtime.New(startTime).Before(m["created_at"].(*gtime.Time)) && m["created_at"].(*gtime.Time).Before(gtime.New(endTime)) { - parseValue, _ := strconv.ParseFloat(m["temperature"].(string), 64) - value += parseValue - num++ - } - } - if value != 0 && num != 0 { - - cityWeatherEchart.Value = fmt.Sprintf("%.2f", value/num) - sumValue += value / num - } else { - cityWeatherEchart.Value = "0.00" - } - cityWeatherEchartOut = append(cityWeatherEchartOut, cityWeatherEchart) - - if types == 2 || types == 3 { - //预报天气 - var foreCastCityWeatherEchart = new(model.CityWeatherEchartOut) - foreCastCityWeatherEchart.Time = strconv.Itoa(duration) + unit - - var foreCastValue float64 - var foreCastNum float64 - //开始时间-1天 - startTime = gtime.New(startTime).AddDate(0, 0, -1).Format("Y-m-d H:i:s") - //结束时间-1天 - endTime = gtime.New(endTime).AddDate(0, 0, -1).Format("Y-m-d H:i:s") - for _, m := range out.List { - if gtime.New(startTime).Before(m["created_at"].(*gtime.Time)) && m["created_at"].(*gtime.Time).Before(gtime.New(endTime)) { - parseValue, _ := strconv.ParseFloat(m["next_day_temp"].(string), 64) - foreCastValue += parseValue - foreCastNum++ - } - } - if foreCastValue != 0 && foreCastNum != 0 { - foreCastCityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastValue/foreCastNum) - foreCastSumValue += foreCastValue / foreCastNum - } else { - foreCastCityWeatherEchart.Value = "0.00" - } - foreCastCityWeatherEchartOut = append(foreCastCityWeatherEchartOut, foreCastCityWeatherEchart) - } - } - - //获取未来一周的时间 - if types == 2 || types == 3 { - //获取最后一条数据 - var lastIn = new(model.TemplateDataLastInput) - lastIn.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - lastParams := map[string]interface{}{ - "CREATED_AT >= ? ": gtime.Now().Format("Y-m-d 00:00:00"), - "CREATED_AT < ? ": gtime.Now().Format("Y-m-d H:i:s"), - "ADCODE = ? ": info.Code, - } - lastIn.Param = lastParams - - lastOut, _ := service.DataTemplate().GetLastData(ctx, lastIn) - if lastOut.Data != nil { - for i := 1; i < 7; i++ { - //获取字段名称 - var field string - switch i { - case 1: - field = "next_day_temp" - break - case 2: - field = "next_three_day_temp" - break - case 3: - field = "next_four_day_temp" - break - case 4: - field = "next_five_day_temp" - break - case 5: - field = "next_six_day_temp" - break - case 6: - field = "next_seven_day_temp" - break - } - parseValue, _ := strconv.ParseFloat(gconv.String(lastOut.Data[field]), 64) - var cityWeatherEchart = new(model.CityWeatherEchartOut) - cityWeatherEchart.Time = strconv.Itoa(gtime.New(gtime.Now()).AddDate(0, 0, i).Day()) + "日" - cityWeatherEchart.Value = fmt.Sprintf("%.2f", parseValue) - foreCastCityWeatherEchartOut = append(foreCastCityWeatherEchartOut, cityWeatherEchart) - foreCastSumValue += parseValue - } - } - } - - //获取实时天气平均值 - for i := 0; i < index; i++ { - var cityWeatherEchart = new(model.CityWeatherEchartOut) - _, _, duration, unit := utils.GetTime(i, types, begin) - cityWeatherEchart.Time = strconv.Itoa(duration) + unit - if sumValue != 0 { - cityWeatherEchart.Value = fmt.Sprintf("%.2f", sumValue/float64(index)) - } else { - cityWeatherEchart.Value = "0.00" - } - avgCityWeatherEchartOut = append(avgCityWeatherEchartOut, cityWeatherEchart) - //获取预报天气值 - if types == 2 || types == 3 { - var foreCastCityWeatherEchart = new(model.CityWeatherEchartOut) - foreCastCityWeatherEchart.Time = strconv.Itoa(duration) + unit - if foreCastSumValue != 0 { - foreCastCityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastSumValue/float64(index+6)) - } else { - foreCastCityWeatherEchart.Value = "0.00" - } - foreCastAvgCityWeatherEchartOut = append(foreCastAvgCityWeatherEchartOut, foreCastCityWeatherEchart) - } - } - //获取未来一周的平均天气 - if types == 2 || types == 3 { - for i := 1; i < 7; i++ { - var cityWeatherEchart = new(model.CityWeatherEchartOut) - cityWeatherEchart.Time = strconv.Itoa(gtime.New(gtime.Now()).AddDate(0, 0, i).Day()) + "日" - cityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastSumValue/float64(gtime.Now().Day()+6)) - foreCastAvgCityWeatherEchartOut = append(foreCastAvgCityWeatherEchartOut, cityWeatherEchart) - } - } - } - } - return -} - -// GetCityWindpowerById 根据ID获取指定城市的风力图表 -func (a *sEnvWeather) GetCityWindpowerById(ctx context.Context, id int, types int) (cityWeatherEchartOut []*model.CityWeatherEchartOut, avgCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastAvgCityWeatherEchartOut []*model.CityWeatherEchartOut, err error) { - info, err := service.CityData().GetInfoById(ctx, id) - if err != nil { - return - } - if info != nil { - //绑定的数据建模 - dataInfo, _ := service.DataTemplateBusi().GetInfo(ctx, consts.Weather) - if dataInfo != nil { - //根据类型获取时间差、开始时间、结束时间 - index, begin, end := utils.GetTimeByType(types) - - //封装数据建模 - var in = new(model.TemplateDataAllInput) - //数据建模ID - in.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - params := map[string]interface{}{ - "CREATED_AT >= ? ": begin, - "CREATED_AT < ? ": end, - "ADCODE = ? ": info.Code, - } - in.Param = params - - //获取数据建模数据 - out, _ := service.DataTemplate().GetAllData(ctx, in) - - var sumValue float64 - var foreCastSumValue float64 - //获取当前数据 - for i := 0; i < index; i++ { - var cityWeatherEchart = new(model.CityWeatherEchartOut) - startTime, endTime, duration, unit := utils.GetTime(i, types, begin) - cityWeatherEchart.Time = strconv.Itoa(duration) + unit - var value float64 - var num float64 - - for _, m := range out.List { - if gtime.New(startTime).Before(m["created_at"].(*gtime.Time)) && m["created_at"].(*gtime.Time).Before(gtime.New(endTime)) { - re := regexp.MustCompile("[0-9]+") - if re.FindAllString(m["windpower"].(string), -1) != nil { - parseValue, _ := strconv.ParseFloat(re.FindAllString(m["windpower"].(string), -1)[0], 64) - value += parseValue - } else { - value += 0.5 - } - num++ - } - } - if value != 0 && num != 0 { - cityWeatherEchart.Value = fmt.Sprintf("%.2f", value/num) - sumValue += value / num - } else { - cityWeatherEchart.Value = "0.00" - } - cityWeatherEchartOut = append(cityWeatherEchartOut, cityWeatherEchart) - - if types == 2 || types == 3 { - //预报天气 - var foreCastCityWeatherEchart = new(model.CityWeatherEchartOut) - foreCastCityWeatherEchart.Time = strconv.Itoa(duration) + unit - - var foreCastValue float64 - var foreCastNum float64 - //开始时间-1天 - startTime = gtime.New(startTime).AddDate(0, 0, -1).Format("Y-m-d H:i:s") - //结束时间-1天 - endTime = gtime.New(endTime).AddDate(0, 0, -1).Format("Y-m-d H:i:s") - for _, m := range out.List { - if gtime.New(startTime).Before(m["created_at"].(*gtime.Time)) && m["created_at"].(*gtime.Time).Before(gtime.New(endTime)) { - re := regexp.MustCompile("[0-9]+") - if re.FindAllString(m["next_day_windpower"].(string), -1) != nil { - parseValue, _ := strconv.ParseFloat(re.FindAllString(m["next_day_windpower"].(string), -1)[0], 64) - foreCastValue += parseValue - } else { - foreCastValue += 0.5 - } - foreCastNum++ - } - } - if foreCastValue != 0 && foreCastNum != 0 { - foreCastCityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastValue/foreCastNum) - foreCastSumValue += foreCastValue / foreCastNum - } else { - foreCastCityWeatherEchart.Value = "0.00" - } - foreCastCityWeatherEchartOut = append(foreCastCityWeatherEchartOut, foreCastCityWeatherEchart) - } - } - - //获取未来一周的时间 - if types == 2 || types == 3 { - //获取最后一条数据 - var lastIn = new(model.TemplateDataLastInput) - lastIn.Id = uint64(dataInfo.DataTemplateId) - //查询条件 - lastParams := map[string]interface{}{ - "CREATED_AT >= ? ": gtime.Now().Format("Y-m-d 00:00:00"), - "CREATED_AT < ? ": gtime.Now().Format("Y-m-d H:i:s"), - "ADCODE = ? ": info.Code, - } - lastIn.Param = lastParams - - lastOut, _ := service.DataTemplate().GetLastData(ctx, lastIn) - if lastOut.Data != nil { - for i := 1; i < 7; i++ { - //获取字段名称 - var field string - switch i { - case 1: - field = "next_day_windpower" - break - case 2: - field = "next_three_day_windpower" - break - case 3: - field = "next_four_day_windpower" - break - case 4: - field = "next_five_day_windpower" - break - case 5: - field = "next_six_day_windpower" - break - case 6: - field = "next_seven_day_windpower" - break - } - var cityWeatherEchart = new(model.CityWeatherEchartOut) - cityWeatherEchart.Time = strconv.Itoa(gtime.New(gtime.Now()).AddDate(0, 0, i).Day()) + "日" - re := regexp.MustCompile("[0-9]+") - var parseValue float64 - if re.FindAllString(gconv.String(lastOut.Data[field]), -1) != nil { - parseValue, _ = strconv.ParseFloat(re.FindAllString(gconv.String(lastOut.Data[field]), -1)[0], 64) - } else { - parseValue += 0.5 - } - cityWeatherEchart.Value = fmt.Sprintf("%.2f", parseValue) - foreCastCityWeatherEchartOut = append(foreCastCityWeatherEchartOut, cityWeatherEchart) - foreCastSumValue += parseValue - } - } - } - - //获取平均值 - for i := 0; i < index; i++ { - var cityWeatherEchart = new(model.CityWeatherEchartOut) - - _, _, duration, unit := utils.GetTime(i, types, begin) - cityWeatherEchart.Time = strconv.Itoa(duration) + unit - - if sumValue != 0 { - cityWeatherEchart.Value = fmt.Sprintf("%.2f", sumValue/float64(index)) - } else { - cityWeatherEchart.Value = "0.00" - } - avgCityWeatherEchartOut = append(avgCityWeatherEchartOut, cityWeatherEchart) - - //获取预报天气值 - if types == 2 || types == 3 { - var foreCastCityWeatherEchart = new(model.CityWeatherEchartOut) - foreCastCityWeatherEchart.Time = strconv.Itoa(duration) + unit - if sumValue != 0 { - foreCastCityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastSumValue/float64(index+6)) - } else { - foreCastCityWeatherEchart.Value = "0.00" - } - foreCastAvgCityWeatherEchartOut = append(foreCastAvgCityWeatherEchartOut, foreCastCityWeatherEchart) - } - } - - //获取未来一周的平均天气 - if types == 2 || types == 3 { - for i := 1; i < 7; i++ { - var cityWeatherEchart = new(model.CityWeatherEchartOut) - cityWeatherEchart.Time = strconv.Itoa(gtime.New(gtime.Now()).AddDate(0, 0, i).Day()) + "日" - cityWeatherEchart.Value = fmt.Sprintf("%.2f", foreCastSumValue/float64(gtime.Now().Day()+6)) - foreCastAvgCityWeatherEchartOut = append(foreCastAvgCityWeatherEchartOut, cityWeatherEchart) - } - } - } - } - return -} diff --git a/internal/logic/logic.go b/internal/logic/logic.go index ea7ddb4..959aa8d 100644 --- a/internal/logic/logic.go +++ b/internal/logic/logic.go @@ -1,19 +1,18 @@ // ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ========================================================================== package logic import ( - _ "github.com/sagoo-cloud/sagooiot/internal/logic/alarm" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/common" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/datahub" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/envirotronics" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/middleware" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/network" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/notice" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/product" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/system" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/tdengine" + _ "sagooiot/internal/logic/alarm" + _ "sagooiot/internal/logic/analysis" + _ "sagooiot/internal/logic/common" + _ "sagooiot/internal/logic/context" + _ "sagooiot/internal/logic/middleware" + _ "sagooiot/internal/logic/network" + _ "sagooiot/internal/logic/notice" + _ "sagooiot/internal/logic/product" + _ "sagooiot/internal/logic/system" + _ "sagooiot/internal/logic/tdengine" ) diff --git a/internal/logic/middleware/middleware.go b/internal/logic/middleware/middleware.go index 8c502d8..96d620a 100644 --- a/internal/logic/middleware/middleware.go +++ b/internal/logic/middleware/middleware.go @@ -1,16 +1,21 @@ package middleware import ( + "context" + "encoding/json" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/i18n/gi18n" "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/net/gtrace" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/response" - "github.com/sagoo-cloud/sagooiot/utility/utils" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/queues" + "sagooiot/internal/service" + "sagooiot/pkg/response" "strings" ) @@ -31,7 +36,6 @@ func New() *sMiddleware { // ResponseHandler 返回处理中间件 func (s *sMiddleware) ResponseHandler(r *ghttp.Request) { r.Middleware.Next() - // 如果已经有返回内容,那么该中间件什么也不做 if r.Response.BufferLength() > 0 { return @@ -57,10 +61,10 @@ func (s *sMiddleware) ResponseHandler(r *ghttp.Request) { } } else { if r.IsAjaxRequest() { - response.JsonExit(r, code.Code(), "", res) + response.Json(r, code.Code(), "", res) } else { // 什么都不做,业务API自行处理模板渲染的成功逻辑。 - response.JsonExit(r, code.Code(), "", res) + response.Json(r, code.Code(), "", res) } } } @@ -68,22 +72,29 @@ func (s *sMiddleware) ResponseHandler(r *ghttp.Request) { // Ctx 自定义上下文对象 func (s *sMiddleware) Ctx(r *ghttp.Request) { ctx := r.GetCtx() - // 初始化登录用户信息 - data, err := service.SysToken().ParseToken(r) - if err != nil { - // 执行下一步请求逻辑 - r.Middleware.Next() - } - if data != nil { - context := new(model.Context) - err = gconv.Struct(data.Data, &context.User) + r.SetCtx(r.GetNeverDoneCtx()) + + if r.GetHeader("Authorization") != "" { + // 初始化登录用户信息 + data, err := service.SysToken().ParseToken(r) if err != nil { - g.Log().Error(ctx, err) // 执行下一步请求逻辑 r.Middleware.Next() } - service.Context().Init(r, context) + if data != nil { + contextModel := new(model.Context) + err = gconv.Struct(data.Data, &contextModel.User) + //请求方式 + contextModel.User.RequestWay = consts.TokenAuth + if err != nil { + g.Log().Error(ctx, err) + // 执行下一步请求逻辑 + r.Middleware.Next() + } + service.Context().Init(r, contextModel) + } } + // 执行下一步请求逻辑 r.Middleware.Next() } @@ -96,9 +107,26 @@ func (s *sMiddleware) Auth(r *ghttp.Request) { return } + //判断是否启用安全控制 + var configDataByIsSecurityControlEnabled *entity.SysConfig + configDataByIsSecurityControlEnabled, _ = service.ConfigData().GetConfigByKey(r.Context(), consts.SysIsSecurityControlEnabled) + sysApiSwitch := 0 + if configDataByIsSecurityControlEnabled != nil && strings.EqualFold(configDataByIsSecurityControlEnabled.ConfigValue, "1") { + //查询API开关是否打开 + sysApiSwitchConfig, _ := service.ConfigData().GetConfigByKey(r.Context(), consts.SysApiSwitch) + if sysApiSwitchConfig != nil { + sysApiSwitch = gconv.Int(sysApiSwitchConfig.ConfigValue) + } + } + + if sysApiSwitch == 0 { + r.Middleware.Next() + return + } + //判断用户是否有访问权限 url := r.Request.URL.Path - if strings.EqualFold(url, "/api/v1/system/user/currentUser") { + if strings.EqualFold(url, "/api/v1/system/user/currentUser") || strings.EqualFold(url, "/api/v1/common/dict/data/list") { r.Middleware.Next() return } @@ -186,38 +214,46 @@ func (s *sMiddleware) Auth(r *ghttp.Request) { } r.Middleware.Next() + } // MiddlewareCORS 跨域处理 func (s *sMiddleware) MiddlewareCORS(r *ghttp.Request) { - + r.SetCtx(r.GetNeverDoneCtx()) //自定义跨域限制 - //corsOptions := r.Response.DefaultCORSOptions() - // you can set options - //corsOptions.AllowDomain = []string{"goframe.org", "baidu.com"} - //r.Response.CORS(corsOptions) - - //采用默认接受所有跨域 - r.Response.CORSDefault() - + corsOptions := r.Response.DefaultCORSOptions() + corsConfig := g.Cfg().MustGet(context.Background(), "server.allowedDomains").Strings() + if corsConfig == nil || len(corsConfig) == 0 { + //采用默认接受所有跨域 + r.Response.CORSDefault() + } else { + corsOptions.AllowDomain = corsConfig + r.Response.CORS(corsOptions) + } r.Middleware.Next() } // OperationLog 操作日志 func (s *sMiddleware) OperationLog(r *ghttp.Request) { - //获取当前登录用户信息 - loginUserId := service.Context().GetUserId(r.GetCtx()) - if loginUserId == 0 { - return + data := service.SysOperLog().AnalysisLog(r.GetCtx()) + + // 写入队列 + logData, _ := json.Marshal(data) + err := queues.ScheduledSysOperLog.Push(context.Background(), consts.QueueDeviceAlarmLogTopic, logData, 10) + if err != nil { + g.Log().Debug(context.TODO(), err) } - var ( - url = r.Request.URL //请求地址 - err = r.GetError() - handlerResponse = r.GetHandlerResponse() - body = r.GetMap() - ) - res := gconv.Map(handlerResponse) +} - service.SysOperLog().Invoke(r.GetCtx(), loginUserId, url, body, r.Method, utils.GetClientIp(r.GetCtx()), res, err) +func (s *sMiddleware) Tracing(r *ghttp.Request) { + _, span := gtrace.NewSpan(r.Context(), r.Method+"_"+r.Request.RequestURI) + defer span.End() + r.Middleware.Next() +} + +func (s *sMiddleware) I18n(r *ghttp.Request) { + lang := r.GetQuery("lang", "zh-CN").String() + r.SetCtx(gi18n.WithLanguage(r.Context(), lang)) + r.Middleware.Next() } diff --git a/internal/logic/network/network_server.go b/internal/logic/network/network_server.go index 9f389c8..a1e255d 100644 --- a/internal/logic/network/network_server.go +++ b/internal/logic/network/network_server.go @@ -2,13 +2,17 @@ package network import ( "context" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/network/core/server" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/core" + "github.com/gogf/gf/v2/os/gtime" ) type sNetworkServer struct{} @@ -57,13 +61,13 @@ func (s *sNetworkServer) GetServerList(ctx context.Context, in *model.GetNetwork // GetServerRunList 获取可运行的服务列表数据 func (s *sNetworkServer) GetServerRunList(ctx context.Context) (list []*model.NetworkServerRes, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.NetworkServer.Ctx(ctx) - err = m.Where(dao.NetworkServer.Columns().Status, 1).Scan(&list) - if err != nil { - err = gerror.New("获取数据失败") - } - }) + + m := dao.NetworkServer.Ctx(ctx) + err = m.Where(dao.NetworkServer.Columns().Status, 1).Scan(&list) + if err != nil { + err = gerror.New("获取数据失败") + } + return } @@ -75,7 +79,37 @@ func (s *sNetworkServer) GetServerById(ctx context.Context, id int) (out *model. // AddServer 添加数据 todo 需要处理 func (s *sNetworkServer) AddServer(ctx context.Context, in model.NetworkServerAddInput) (err error) { - insertResult, insertResultErr := dao.NetworkServer.Ctx(ctx).Insert(in) + //查询服务器名称是否存在 + num, err := dao.NetworkServer.Ctx(ctx).Where(g.Map{dao.NetworkServer.Columns().Name: in.Name}).Count() + if err != nil { + return + } + if num > 0 { + err = gerror.New("服务器名称已存在") + return + } + + insertResult, insertResultErr := dao.NetworkServer.Ctx(ctx).Data(do.NetworkServer{ + DeptId: service.Context().GetUserDeptId(ctx), + Name: in.Name, + Types: in.Types, + Addr: in.Addr, + Register: in.Register, + Heartbeat: in.Heartbeat, + Protocol: in.Protocol, + Devices: in.Devices, + Status: in.Status, + CreatedAt: gtime.Now(), + CreateBy: in.CreateBy, + Remark: in.Remark, + IsTls: in.IsTls, + AuthType: in.AuthType, + AuthUser: in.AuthUser, + AuthPasswd: in.AuthPasswd, + AccessToken: in.AccessToken, + CertificateId: in.CertificateId, + Stick: in.Stick, + }).Insert() if insertResultErr != nil { return insertResultErr } @@ -84,19 +118,42 @@ func (s *sNetworkServer) AddServer(ctx context.Context, in model.NetworkServerAd return lastIdErr } if err == nil && in.Status == consts.ServerStatusOnline { - return core.LoadServer(ctx, int(lastId)) + return server.LoadServer(ctx, int(lastId)) } return } // EditServer 修改数据 todo 需要处理 func (s *sNetworkServer) EditServer(ctx context.Context, in model.NetworkServerEditInput) (err error) { + //根据ID获取服务数据 + var netWorkServer *entity.NetworkServer + err = dao.NetworkServer.Ctx(ctx).Where(dao.NetworkServer.Columns().Id, in.Id).Scan(&netWorkServer) + if err != nil { + return err + } + if netWorkServer == nil { + return gerror.New("ID错误") + } + + //查询服务器名称是否存在 + num, err := dao.NetworkServer.Ctx(ctx).Where(g.Map{dao.NetworkServer.Columns().Name: in.Name}).WhereNot(dao.NetworkServer.Columns().Id, in.Id).Count() + if err != nil { + return + } + if num > 0 { + err = gerror.New("服务器名称已存在") + return + } + _, err = dao.NetworkServer.Ctx(ctx).FieldsEx(dao.NetworkServer.Columns().Id, dao.NetworkServer.Columns().CreateBy).Where(dao.NetworkServer.Columns().Id, in.Id).Update(in) - if err = core.RemoveServer(in.Id); err != nil { + if err != nil { + return err + } + if err = server.RemoveServer(in.Id); err != nil { return err } if in.Status == 1 { - if err = core.LoadServer(ctx, in.Id); err != nil { + if err = server.LoadServer(ctx, in.Id); err != nil { return err } } @@ -106,10 +163,21 @@ func (s *sNetworkServer) EditServer(ctx context.Context, in model.NetworkServerE // 删除数据 // todo 需要处理 func (s *sNetworkServer) DeleteServer(ctx context.Context, ids []int) (err error) { + + for _, id := range ids { + var netWorkServer *entity.NetworkServer + err = dao.NetworkServer.Ctx(ctx).Where(dao.NetworkServer.Columns().Id, id).Scan(&netWorkServer) + if err != nil { + return err + } + if netWorkServer == nil { + return gerror.New("ID错误") + } + } _, err = dao.NetworkServer.Ctx(ctx).Delete(dao.NetworkServer.Columns().Id+" in (?)", ids) if err == nil { for _, node := range ids { - if err = core.RemoveServer(node); err != nil { + if err = server.RemoveServer(node); err != nil { return err } } @@ -119,6 +187,15 @@ func (s *sNetworkServer) DeleteServer(ctx context.Context, ids []int) (err error // SetServerStatus 修改状态数据 todo 需要处理 func (s *sNetworkServer) SetServerStatus(ctx context.Context, id, status int) (err error) { + //根据ID获取服务数据 + var netWorkServer *entity.NetworkServer + err = dao.NetworkServer.Ctx(ctx).Where(dao.NetworkServer.Columns().Id, id).Scan(&netWorkServer) + if err != nil { + return err + } + if netWorkServer == nil { + return gerror.New("ID错误") + } var data = g.Map{ dao.NetworkServer.Columns().Status: status, @@ -128,11 +205,11 @@ func (s *sNetworkServer) SetServerStatus(ctx context.Context, id, status int) (e return err } if status == 1 { - if err = core.LoadServer(ctx, id); err != nil { + if err = server.LoadServer(ctx, id); err != nil { return err } } else { - if err = core.RemoveServer(id); err != nil { + if err = server.RemoveServer(id); err != nil { return err } } diff --git a/internal/logic/network/network_tunnel.go b/internal/logic/network/network_tunnel.go index d19501b..5d8e2a5 100644 --- a/internal/logic/network/network_tunnel.go +++ b/internal/logic/network/network_tunnel.go @@ -2,12 +2,15 @@ package network import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/core" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/network/core/tunnel" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -23,16 +26,15 @@ func init() { service.RegisterNetworkTunnel(sNetworkTunnelNew()) } -const ( - TunnelIsOffline = iota - TunnelIsOnLine -) - -// 获取列表数据 +// GetTunnelList 获取列表数据 func (s *sNetworkTunnel) GetTunnelList(ctx context.Context, in *model.GetNetworkTunnelListInput) (total int, out []*model.NetworkTunnelOut, err error) { err = g.Try(ctx, func(ctx context.Context) { m := dao.NetworkTunnel.Ctx(ctx) + if in.PaginationInput == nil { + in.PaginationInput = &model.PaginationInput{} + } + if in.ServiceId > 0 { m = m.Where(dao.NetworkTunnel.Columns().ServerId, in.ServiceId) } @@ -64,48 +66,96 @@ func (s *sNetworkTunnel) GetTunnelList(ctx context.Context, in *model.GetNetwork return } -// 获取列表数据 +// GetTunnelRunList 获取列表数据 func (s *sNetworkTunnel) GetTunnelRunList(ctx context.Context) (out []*model.NetworkTunnelOut, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.NetworkTunnel.Ctx(ctx) - err = m.Where(dao.NetworkTunnel.Columns().Status, 1).Scan(&out) - if err != nil { - err = gerror.New("获取数据失败") - } - }) + m := dao.NetworkTunnel.Ctx(ctx) + err = m.Where(dao.NetworkTunnel.Columns().Status, 1).Scan(&out) + if err != nil { + err = gerror.New("获取数据失败") + } + return } // 获取指定ID数据 func (s *sNetworkTunnel) GetTunnelById(ctx context.Context, id int) (out *model.NetworkTunnelOut, err error) { - err = dao.NetworkTunnel.Ctx(ctx).Where("id", id).Scan(&out) + err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Id, id).Scan(&out) return } // TODO 这里更改了请求参数,需要确认是否ok -// 添加数据 +// AddTunnel 添加数据 func (s *sNetworkTunnel) AddTunnel(ctx context.Context, in model.NetworkTunnelAddInput) (id int, err error) { - rs, err := dao.NetworkTunnel.Ctx(ctx).Insert(in) + //查询通道名称是否存在 + num, err := dao.NetworkTunnel.Ctx(ctx).Where(g.Map{dao.NetworkTunnel.Columns().Name: in.Name}).Count() if err != nil { return } - newId, _ := rs.LastInsertId() - id = int(newId) - - if err == nil && in.Status == TunnelIsOnLine { - var networkTunnelEditInput model.NetworkTunnelEditInput - if err = dao.NetworkTunnel.Ctx(ctx).Where("name", in.Name).Scan(&networkTunnelEditInput); err != nil { - return - } else { - err = core.LoadTunnel(ctx, networkTunnelEditInput.Id) + if num > 0 { + err = gerror.New("通道名称已存在") + return + } + err = dao.NetworkTunnel.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + rs, err := dao.NetworkTunnel.Ctx(ctx).Data(do.NetworkTunnel{ + DeptId: service.Context().GetUserDeptId(ctx), + ServerId: in.ServerId, + Name: in.Name, + Types: in.Types, + Addr: in.Addr, + Remote: in.Remote, + Retry: in.Retry, + Heartbeat: in.Heartbeat, + Serial: in.Serial, + Protoccol: in.Protocol, + Status: in.Status, + CreatedAt: gtime.Now(), + Remark: in.Remark, + }).Insert() + if err != nil { return } + newId, _ := rs.LastInsertId() + id = int(newId) + + if err == nil && in.Status == consts.TunnelIsOnLine { + var networkTunnelEditInput model.NetworkTunnelEditInput + if err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Name, in.Name).Scan(&networkTunnelEditInput); err != nil { + return + } else { + err = tunnel.LoadTunnel(ctx, networkTunnelEditInput.Id) + return + } + } + + return + }) + if err != nil { + return 0, err } return } -// 修改数据 +// EditTunnel 修改数据 func (s *sNetworkTunnel) EditTunnel(ctx context.Context, in model.NetworkTunnelEditInput) (err error) { + var netWorkTunnel *entity.NetworkTunnel + err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Id, in.Id).Scan(&netWorkTunnel) + if err != nil { + return err + } + if netWorkTunnel == nil { + return gerror.New("ID错误") + } + + //查询通道名称是否存在 + num, err := dao.NetworkTunnel.Ctx(ctx).Where(g.Map{dao.NetworkTunnel.Columns().Name: in.Name}).WhereNot(dao.NetworkTunnel.Columns().Id, in.Id).Count() + if err != nil { + return + } + if num > 0 { + err = gerror.New("通道名称已存在") + return + } + var param do.NetworkTunnel err = gconv.Scan(in, ¶m) if err != nil { @@ -116,28 +166,39 @@ func (s *sNetworkTunnel) EditTunnel(ctx context.Context, in model.NetworkTunnelE param.Remote = nil } - _, err = dao.NetworkTunnel.Ctx(ctx).FieldsEx(dao.NetworkTunnel.Columns().Id).Where(dao.NetworkTunnel.Columns().Id, in.Id).Update(param) + _, err = dao.NetworkTunnel.Ctx(ctx).Data(param).Where(dao.NetworkTunnel.Columns().Id, in.Id).Update() if err != nil { return } - if err = core.RemoveTunnel(in.Id); err != nil { + if err = tunnel.RemoveTunnel(in.Id); err != nil { return err } - if in.Status == TunnelIsOnLine { - if err = core.LoadTunnel(ctx, in.Id); err != nil { + if in.Status == consts.TunnelIsOnLine { + if err = tunnel.LoadTunnel(ctx, in.Id); err != nil { return err } } return } -// 删除数据 +// DeleteTunnel 删除数据 func (s *sNetworkTunnel) DeleteTunnel(ctx context.Context, ids []int) (err error) { + for _, id := range ids { + var netWorkTunnel *entity.NetworkTunnel + err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Id, id).Scan(&netWorkTunnel) + if err != nil { + return err + } + if netWorkTunnel == nil { + return gerror.New("ID错误") + } + } + _, err = dao.NetworkTunnel.Ctx(ctx).Delete(dao.NetworkTunnel.Columns().Id+" in (?)", ids) //TODO 这里需要注意中间删除失败的情况 if err == nil { for _, node := range ids { - if err = core.RemoveTunnel(node); err != nil { + if err = tunnel.RemoveTunnel(node); err != nil { return err } } @@ -145,24 +206,34 @@ func (s *sNetworkTunnel) DeleteTunnel(ctx context.Context, ids []int) (err error return } -// 修改状态数据 +// SetTunnelStatus 修改状态数据 func (s *sNetworkTunnel) SetTunnelStatus(ctx context.Context, id, status int) (err error) { + + var netWorkTunnel *entity.NetworkTunnel + err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Id, id).Scan(&netWorkTunnel) + if err != nil { + return err + } + if netWorkTunnel == nil { + return gerror.New("ID错误") + } + var data = g.Map{ dao.NetworkTunnel.Columns().Status: status, } //TODO 这儿里还需要进行通道的启用处理,启用成功更新数据状态 - _, err = dao.NetworkTunnel.Ctx(ctx).Where(dao.NetworkTunnel.Columns().Id, id).Update(data) + _, err = dao.NetworkTunnel.Ctx(ctx).Data(data).Where(dao.NetworkTunnel.Columns().Id, id).Update() if err != nil { return } if status == 0 { - if err = core.LoadTunnel(ctx, id); err != nil { + if err = tunnel.RemoveTunnel(id); err != nil { return err } } else { - if err = core.RemoveTunnel(id); err != nil { + if err = tunnel.LoadTunnel(ctx, id); err != nil { return err } } diff --git a/internal/logic/notice/notice_config.go b/internal/logic/notice/notice_config.go index a41ed7b..8b1c0bb 100644 --- a/internal/logic/notice/notice_config.go +++ b/internal/logic/notice/notice_config.go @@ -2,13 +2,16 @@ package notice import ( "context" + "errors" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/guid" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" ) type sNoticeConfig struct{} @@ -20,7 +23,7 @@ func init() { service.RegisterNoticeConfig(sNoticeConfigNew()) } -//GetNoticeConfigList 获取列表数据 +// GetNoticeConfigList 获取列表数据 func (s *sNoticeConfig) GetNoticeConfigList(ctx context.Context, in *model.GetNoticeConfigListInput) (total, page int, list []*model.NoticeConfigOutput, err error) { err = g.Try(ctx, func(ctx context.Context) { m := dao.NoticeConfig.Ctx(ctx) @@ -34,6 +37,7 @@ func (s *sNoticeConfig) GetNoticeConfigList(ctx context.Context, in *model.GetNo if in.SendGateway != "" { m = m.Where(dao.NoticeConfig.Columns().SendGateway, in.SendGateway) } + total, err = m.Count() if err != nil { err = gerror.New("获取总行数失败") @@ -51,30 +55,45 @@ func (s *sNoticeConfig) GetNoticeConfigList(ctx context.Context, in *model.GetNo return } -//GetNoticeConfigById 获取指定ID数据 -func (s *sNoticeConfig) GetNoticeConfigById(ctx context.Context, id int) (out *model.NoticeConfigOutput, err error) { +// GetNoticeConfigById 获取指定ID数据 +func (s *sNoticeConfig) GetNoticeConfigById(ctx context.Context, id string) (out *model.NoticeConfigOutput, err error) { err = dao.NoticeConfig.Ctx(ctx).Where(dao.NoticeConfig.Columns().Id, id).Scan(&out) return } -//AddNoticeConfig 添加数据 +// AddNoticeConfig 添加数据 func (s *sNoticeConfig) AddNoticeConfig(ctx context.Context, in model.NoticeConfigAddInput) (err error) { - _, err = dao.NoticeConfig.Ctx(ctx).Insert(in) + _, err = dao.NoticeConfig.Ctx(ctx).Data(do.NoticeConfig{ + Id: guid.S(), + DeptId: service.Context().GetUserDeptId(ctx), + Title: in.Title, + SendGateway: in.SendGateway, + Types: in.Types, + CreatedAt: gtime.Now(), + }).Insert() return } -//EditNoticeConfig 修改数据 +// EditNoticeConfig 修改数据 func (s *sNoticeConfig) EditNoticeConfig(ctx context.Context, in model.NoticeConfigEditInput) (err error) { + noticeConfig, err := s.GetNoticeConfigById(ctx, in.Id) + if err != nil { + return + } + if noticeConfig == nil { + return gerror.New("通知配置不存在") + } + _, err = dao.NoticeConfig.Ctx(ctx).FieldsEx(dao.NoticeConfig.Columns().Id).Where(dao.NoticeConfig.Columns().Id, in.Id).Update(in) return } -//DeleteNoticeConfig 删除数据 +// DeleteNoticeConfig 删除数据 func (s *sNoticeConfig) DeleteNoticeConfig(ctx context.Context, Ids []string) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.NoticeConfig.Ctx(ctx).Where(dao.NoticeConfig.Columns().Id+" in(?)", Ids).Delete() - liberr.ErrIsNil(ctx, err, "删除配置数据失败") - }) + _, err = dao.NoticeConfig.Ctx(ctx).Where(dao.NoticeConfig.Columns().Id+" in(?)", Ids).Delete() + if err != nil { + return errors.New("删除失败") + } return } diff --git a/internal/logic/notice/notice_info.go b/internal/logic/notice/notice_info.go index 5e6bef3..1f0e5b9 100644 --- a/internal/logic/notice/notice_info.go +++ b/internal/logic/notice/notice_info.go @@ -2,13 +2,14 @@ package notice import ( "context" + "errors" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" ) type sNoticeInfo struct{} @@ -20,70 +21,81 @@ func init() { service.RegisterNoticeInfo(sNoticeInfoNew()) } -//GetNoticeInfoList 获取列表数据 +// GetNoticeInfoList 获取列表数据 func (s *sNoticeInfo) GetNoticeInfoList(ctx context.Context, in *model.GetNoticeInfoListInput) (total, page int, list []*model.NoticeInfoOutput, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.NoticeInfo.Ctx(ctx) + m := dao.NoticeInfo.Ctx(ctx) - if in.KeyWord != "" { - m = m.WhereLike(dao.NoticeInfo.Columns().MsgTitle, "%"+in.KeyWord+"%") - m = m.WhereOrLike(dao.NoticeInfo.Columns().MsgBody, "%"+in.KeyWord+"%") - } - if in.Method != "" { - m = m.Where(dao.NoticeInfo.Columns().Method, in.Method) - } - if in.ConfigId != "" { - m = m.Where(dao.NoticeInfo.Columns().ConfigId, in.ConfigId) - } + if in.KeyWord != "" { + m = m.WhereLike(dao.NoticeInfo.Columns().MsgTitle, "%"+in.KeyWord+"%") + m = m.WhereOrLike(dao.NoticeInfo.Columns().MsgBody, "%"+in.KeyWord+"%") + } + if in.Method != "" { + m = m.Where(dao.NoticeInfo.Columns().Method, in.Method) + } + if in.ConfigId != "" { + m = m.Where(dao.NoticeInfo.Columns().ConfigId, in.ConfigId) + } - if in.ComeFrom != "" { - m = m.Where(dao.NoticeInfo.Columns().ComeFrom, in.ComeFrom) - } + if in.ComeFrom != "" { + m = m.Where(dao.NoticeInfo.Columns().ComeFrom, in.ComeFrom) + } - if in.Status != -1 { - m = m.Where(dao.NoticeInfo.Columns().Status, in.Status) - } - - total, err = m.Count() - if err != nil { - err = gerror.New("获取总行数失败") - return - } - page = in.PageNum - if in.PageSize == 0 { - in.PageSize = consts.PageSize - } - err = m.Page(page, in.PageSize).Order("created_at desc").Scan(&list) - if err != nil { - err = gerror.New("获取数据失败") - } - }) + if in.Status != -1 { + m = m.Where(dao.NoticeInfo.Columns().Status, in.Status) + } + total, err = m.Count() + if err != nil { + err = gerror.New("获取总行数失败") + return + } + page = in.PageNum + if in.PageSize == 0 { + in.PageSize = consts.PageSize + } + err = m.Page(page, in.PageSize).Order("created_at desc").Scan(&list) + if err != nil { + err = gerror.New("获取数据失败") + } return } -//GetNoticeInfoById 获取指定ID数据 +// GetNoticeInfoById 获取指定ID数据 func (s *sNoticeInfo) GetNoticeInfoById(ctx context.Context, id int) (out *model.NoticeInfoOutput, err error) { err = dao.NoticeInfo.Ctx(ctx).Where(dao.NoticeInfo.Columns().Id, id).Scan(&out) return } -//AddNoticeInfo 添加数据 +// AddNoticeInfo 添加数据 func (s *sNoticeInfo) AddNoticeInfo(ctx context.Context, in model.NoticeInfoAddInput) (err error) { - _, err = dao.NoticeInfo.Ctx(ctx).Insert(in) + _, err = dao.NoticeInfo.Ctx(ctx).Data(do.NoticeInfo{ + ConfigId: in.ConfigId, + ComeFrom: in.ComeFrom, + Method: in.Method, + MsgTitle: in.MsgTitle, + MsgBody: in.MsgBody, + MsgUrl: in.MsgUrl, + UserIds: in.UserIds, + OrgIds: in.OrgIds, + Totag: in.Totag, + Status: in.Status, + MethodCron: in.MethodCron, + MethodNum: in.MethodNum, + CreatedAt: gtime.Now(), + }).Insert() return } -//EditNoticeInfo 修改数据 +// EditNoticeInfo 修改数据 func (s *sNoticeInfo) EditNoticeInfo(ctx context.Context, in model.NoticeInfoEditInput) (err error) { _, err = dao.NoticeInfo.Ctx(ctx).FieldsEx(dao.NoticeInfo.Columns().Id).Where(dao.NoticeInfo.Columns().Id, in.Id).Update(in) return } -//DeleteNoticeInfo 删除数据 +// DeleteNoticeInfo 删除数据 func (s *sNoticeInfo) DeleteNoticeInfo(ctx context.Context, Ids []int) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.NoticeInfo.Ctx(ctx).Where(dao.NoticeInfo.Columns().Id+" in(?)", Ids).Delete() - liberr.ErrIsNil(ctx, err, "删除通知数据失败") - }) + _, err = dao.NoticeInfo.Ctx(ctx).Where(dao.NoticeInfo.Columns().Id+" in(?)", Ids).Delete() + if err != nil { + return errors.New("删除通知数据失败") + } return } diff --git a/internal/logic/notice/notice_log.go b/internal/logic/notice/notice_log.go index 180e6e5..7965f3a 100644 --- a/internal/logic/notice/notice_log.go +++ b/internal/logic/notice/notice_log.go @@ -2,9 +2,12 @@ package notice import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/errors/gerror" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" ) type sNoticeLog struct{} @@ -17,19 +20,54 @@ func noticeLogNew() *sNoticeLog { return &sNoticeLog{} } -// 通知日志记录 +// Add 通知日志记录 func (s *sNoticeLog) Add(ctx context.Context, in *model.NoticeLogAddInput) (err error) { - _, err = dao.NoticeLog.Ctx(ctx).Data(in).Insert() + template, err := service.NoticeTemplate().GetNoticeTemplateById(ctx, in.TemplateId) + if err != nil { + return + } + if template == nil { + return gerror.New("模板不存在") + } + + _, err = dao.NoticeLog.Ctx(ctx).Data(do.NoticeLog{ + DeptId: template.DeptId, + SendGateway: in.SendGateway, + TemplateId: in.TemplateId, + Addressee: in.Addressee, + Title: in.Title, + Content: in.Content, + Status: in.Status, + FailMsg: in.FailMsg, + SendTime: in.SendTime, + }).Insert() return } -// 删除日志 +// Del 删除日志 func (s *sNoticeLog) Del(ctx context.Context, ids []uint64) (err error) { + for _, id := range ids { + var noticeLog *entity.NoticeLog + noticeLog, err = s.GetInfoById(ctx, id) + if err != nil { + return + } + if noticeLog == nil { + return gerror.New("ID错误") + } + + } _, err = dao.NoticeLog.Ctx(ctx).WhereIn(dao.NoticeLog.Columns().Id, ids).Delete() return } -// 搜索 +// GetInfoById 获取日志信息 +func (s *sNoticeLog) GetInfoById(ctx context.Context, id uint64) (out *entity.NoticeLog, err error) { + err = dao.NoticeLog.Ctx(ctx).Where(dao.NoticeLog.Columns().Id, id).Scan(&out) + return +} + +// Search 搜索 func (s *sNoticeLog) Search(ctx context.Context, in *model.NoticeLogSearchInput) (out *model.NoticeLogSearchOutput, err error) { out = new(model.NoticeLogSearchOutput) m := dao.NoticeLog.Ctx(ctx) @@ -41,6 +79,7 @@ func (s *sNoticeLog) Search(ctx context.Context, in *model.NoticeLogSearchInput) } out.CurrentPage = in.PageNum + if out.Total, err = m.Count(); err != nil || out.Total == 0 { return } @@ -65,7 +104,7 @@ func (s *sNoticeLog) Search(ctx context.Context, in *model.NoticeLogSearchInput) return } -//ClearLogByDays 按日期删除日志 +// ClearLogByDays 按日期删除日志 func (s *sNoticeLog) ClearLogByDays(ctx context.Context, days int) (err error) { _, err = dao.NoticeLog.Ctx(ctx).Delete("to_days(now())-to_days(`send_time`) > ?", days+1) return diff --git a/internal/logic/notice/notice_template.go b/internal/logic/notice/notice_template.go index 260a69e..d9a4e90 100644 --- a/internal/logic/notice/notice_template.go +++ b/internal/logic/notice/notice_template.go @@ -2,13 +2,16 @@ package notice import ( "context" + "errors" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" ) type sNoticeTemplate struct{} @@ -20,7 +23,7 @@ func init() { service.RegisterNoticeTemplate(sNoticeTemplateNew()) } -//GetNoticeTemplateList 获取列表数据 +// GetNoticeTemplateList 获取列表数据 func (s *sNoticeTemplate) GetNoticeTemplateList(ctx context.Context, in *model.GetNoticeTemplateListInput) (total, page int, list []*model.NoticeTemplateOutput, err error) { err = g.Try(ctx, func(ctx context.Context) { m := dao.NoticeTemplate.Ctx(ctx) @@ -57,41 +60,85 @@ func (s *sNoticeTemplate) GetNoticeTemplateList(ctx context.Context, in *model.G return } -//GetNoticeTemplateById 获取指定ID数据 +// GetNoticeTemplateById 获取指定ID数据 func (s *sNoticeTemplate) GetNoticeTemplateById(ctx context.Context, id string) (out *model.NoticeTemplateOutput, err error) { - err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().Id, id).Scan(&out) + err = dao.NoticeTemplate.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: 0, + Name: "NoticeTemplateById:" + id, + Force: false, + }).Where(dao.NoticeTemplate.Columns().Id, id).Scan(&out) return } -//GetNoticeTemplateByConfigId 获取指定ConfigID数据 +// GetNoticeTemplateByConfigId 获取指定ConfigID数据 func (s *sNoticeTemplate) GetNoticeTemplateByConfigId(ctx context.Context, configId string) (out *model.NoticeTemplateOutput, err error) { err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().ConfigId, configId).Scan(&out) return } -//AddNoticeTemplate 添加数据 +// AddNoticeTemplate 添加数据 func (s *sNoticeTemplate) AddNoticeTemplate(ctx context.Context, in model.NoticeTemplateAddInput) (err error) { - _, err = dao.NoticeTemplate.Ctx(ctx).Insert(in) + _, err = dao.NoticeTemplate.Ctx(ctx).Data(do.NoticeTemplate{ + DeptId: service.Context().GetUserDeptId(ctx), + ConfigId: in.ConfigId, + SendGateway: in.SendGateway, + Code: in.Code, + Title: in.Title, + Content: in.Content, + CreatedAt: gtime.Now(), + }).Insert() return } -//EditNoticeTemplate 修改数据 +// EditNoticeTemplate 修改数据 func (s *sNoticeTemplate) EditNoticeTemplate(ctx context.Context, in model.NoticeTemplateEditInput) (err error) { + template, err := s.GetNoticeTemplateById(ctx, in.Id) + if err != nil { + return + } + if template == nil { + return gerror.New("模板不存在") + } + _, err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().Id, in.Id).Update(in) return } -//SaveNoticeTemplate 直接更新数据 +// SaveNoticeTemplate 直接更新数据 func (s *sNoticeTemplate) SaveNoticeTemplate(ctx context.Context, in model.NoticeTemplateAddInput) (err error) { - _, err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().ConfigId, in.ConfigId).Save(in) + template, err := s.GetNoticeTemplateById(ctx, in.Id) + if err != nil { + return + } + if template == nil { + in.DeptId = service.Context().GetUserDeptId(ctx) + } + _, err = dao.NoticeTemplate.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: -1, + Name: "NoticeTemplateById:" + in.Id, + Force: false, + }).Where(dao.NoticeTemplate.Columns().ConfigId, in.ConfigId).Save(in) return } -//DeleteNoticeTemplate 删除数据 +// DeleteNoticeTemplate 删除数据 func (s *sNoticeTemplate) DeleteNoticeTemplate(ctx context.Context, Ids []string) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().Id+" in(?)", Ids).Delete() - liberr.ErrIsNil(ctx, err, "删除模版数据失败") - }) + + for _, id := range Ids { + var template *model.NoticeTemplateOutput + template, err = s.GetNoticeTemplateById(ctx, id) + if err != nil { + return + } + if template == nil { + return gerror.New("模板不存在") + } + + } + _, err = dao.NoticeTemplate.Ctx(ctx).Where(dao.NoticeTemplate.Columns().Id+" in(?)", Ids).Delete() + if err != nil { + return errors.New("删除模版数据失败") + } + return } diff --git a/internal/logic/product/dev_category.go b/internal/logic/product/dev_category.go index e879db2..6ea6c6f 100644 --- a/internal/logic/product/dev_category.go +++ b/internal/logic/product/dev_category.go @@ -2,12 +2,12 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" @@ -45,6 +45,7 @@ func (s *sDevCategory) GetNameByIds(ctx context.Context, categoryIds []uint) (na err = dao.DevProductCategory.Ctx(ctx). Fields(c.Id, c.Name). WhereIn(c.Id, categoryIds). + OrderAsc(c.Sort). OrderDesc(c.Id). Scan(&categorys) if err != nil || len(categorys) == 0 { @@ -65,46 +66,40 @@ func (s *sDevCategory) GetNameByIds(ctx context.Context, categoryIds []uint) (na return } +// ListForPage 产品分类列表 func (s *sDevCategory) ListForPage(ctx context.Context, page, limit int, name string) (out []*model.ProductCategoryTreeOutput, total int, err error) { if page < 1 { page = 1 } if limit < 1 { - limit = consts.DefaultPageSize + limit = consts.PageSize } - m := dao.DevProductCategory.Ctx(ctx).Fields("id"). - Where(dao.DevProductCategory.Columns().ParentId, 0). + m := dao.DevProductCategory.Ctx(ctx). + OrderAsc(dao.DevProductCategory.Columns().Sort). OrderDesc(dao.DevProductCategory.Columns().Id) if name != "" { m = m.WhereLike(dao.DevProductCategory.Columns().Name, "%"+name+"%") } - total, _ = m.Count() - if total > 0 { - ids, _ := m.Page(page, limit).Array() - - var categorys []*entity.DevProductCategory - - err = dao.DevProductCategory.Ctx(ctx). - WhereIn(dao.DevProductCategory.Columns().Id, ids). - WhereOr(dao.DevProductCategory.Columns().ParentId, ids). - OrderDesc(dao.DevProductCategory.Columns().Id). - Scan(&categorys) - if err != nil || len(categorys) == 0 { - return - } - - out = Tree(categorys, 0) + var categorys []*entity.DevProductCategory + err = m.Scan(&categorys) + if err != nil { + return } + out = Tree(categorys, 0) + total = len(out) + start := (page - 1) * limit + end := page * limit + out = out[start:end] return } func (s *sDevCategory) List(ctx context.Context, name string) (out []*model.ProductCategoryTreeOutput, err error) { var categorys []*entity.DevProductCategory - m := dao.DevProductCategory.Ctx(ctx).OrderDesc(dao.DevProductCategory.Columns().Id) + m := dao.DevProductCategory.Ctx(ctx).OrderAsc(dao.DevProductCategory.Columns().Sort).OrderDesc(dao.DevProductCategory.Columns().Id) if name != "" { m = m.WhereLike(dao.DevProductCategory.Columns().Name, "%"+name+"%") } @@ -114,7 +109,13 @@ func (s *sDevCategory) List(ctx context.Context, name string) (out []*model.Prod return } - out = Tree(categorys, 0) + if name != "" { + if err = gconv.Scan(categorys, &out); err != nil { + return + } + } else { + out = Tree(categorys, 0) + } return } @@ -139,11 +140,13 @@ func (s *sDevCategory) Add(ctx context.Context, in *model.AddProductCategoryInpu loginUserId := service.Context().GetUserId(ctx) _, err = dao.DevProductCategory.Ctx(ctx).Data(do.DevProductCategory{ - ParentId: in.ParentId, - Key: in.Key, - Name: in.Name, - Desc: in.Desc, - CreateBy: uint(loginUserId), + DeptId: service.Context().GetUserDeptId(ctx), + ParentId: in.ParentId, + Key: in.Key, + Name: in.Name, + Sort: in.Sort, + Desc: in.Desc, + CreatedBy: uint(loginUserId), }).Insert() if err != nil { return @@ -163,14 +166,19 @@ func (s *sDevCategory) Edit(ctx context.Context, in *model.EditProductCategoryIn return gerror.New("分类不存在") } + if category.Id == in.ParentId { + return gerror.New("上级分类节点选择错误,请重新选择!") + } //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) _, err = dao.DevProductCategory.Ctx(ctx).Data(do.DevProductCategory{ - Key: in.Key, - Name: in.Name, - Desc: in.Desc, - UpdateBy: uint(loginUserId), + ParentId: in.ParentId, + Key: in.Key, + Name: in.Name, + Sort: in.Sort, + Desc: in.Desc, + UpdatedBy: uint(loginUserId), }).Where(dao.DevProductCategory.Columns().Id, in.Id).Update() return diff --git a/internal/logic/product/dev_data_report.go b/internal/logic/product/dev_data_report.go index 2cdb659..58f2cf9 100644 --- a/internal/logic/product/dev_data_report.go +++ b/internal/logic/product/dev_data_report.go @@ -2,8 +2,11 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "errors" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/dcache" ) type sDevDataReport struct{} @@ -16,13 +19,39 @@ func devDataReport() *sDevDataReport { return &sDevDataReport{} } -//Event 设备事件上报 -func (s *sDevDataReport) Event(ctx context.Context, deviceKey string, data map[string]any) error { - dout, err := service.DevDevice().Get(ctx, deviceKey) +// Event 设备事件上报 +func (s *sDevDataReport) Event(ctx context.Context, deviceKey string, data model.ReportEventData, subKey ...string) error { + device, err := dcache.GetDeviceDetailInfo(deviceKey) if err != nil { return err } + if device == nil { + return errors.New("未发现设备") + } + + err = service.AlarmRule().Check(ctx, device.Product.Key, deviceKey, consts.AlarmTriggerTypeEvent, data, subKey...) - err = service.AlarmRule().Check(ctx, dout.Product.Key, deviceKey, model.AlarmTriggerTypeEvent, data) return err } + +// Property 设备属性上报 +func (s *sDevDataReport) Property(ctx context.Context, deviceKey string, data model.ReportPropertyData, subKey ...string) error { + //logC, err := json.Marshal(data) + //if err != nil { + // g.Log().Error(ctx, err) + //} + //g.Log().Debugf(ctx, "dev_data_report: deveceKey(%s), subKey(%s), data(%s)", deviceKey, strings.Join(subKey, ","), logC) + + //if len(subKey) > 0 { + // skey := subKey[0] + // service.DevDevice().UpdateStatus(ctx, skey) + //} else { + // service.DevDevice().UpdateStatus(ctx, deviceKey) + //} + + if err := service.TSLTable().Insert(ctx, deviceKey, data, subKey...); err != nil { + return err + } + + return nil +} diff --git a/internal/logic/product/dev_data_report_test.go b/internal/logic/product/dev_data_report_test.go index c59058c..4069109 100644 --- a/internal/logic/product/dev_data_report_test.go +++ b/internal/logic/product/dev_data_report_test.go @@ -2,19 +2,22 @@ package product import ( "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/alarm" - "github.com/sagoo-cloud/sagooiot/internal/service" + _ "sagooiot/internal/logic/alarm" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" + "time" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" ) func TestEvent(t *testing.T) { - deviceKey := "device1" - data := map[string]any{ - "aaa": map[string]any{ - "a": 111, - "b": 222, + deviceKey := "t20221333" + data := model.ReportEventData{ + Key: "aaa", + Param: model.ReportEventParam{ + Value: map[string]any{"a": 111, "b": 222}, + CreateTime: time.Now().Unix(), }, } err := service.DevDataReport().Event(context.TODO(), deviceKey, data) @@ -22,3 +25,18 @@ func TestEvent(t *testing.T) { t.Fatal(err) } } + +func TestProperty(t *testing.T) { + deviceKey := "aoxiang_d_gw" + subKey := "aoxiang_d_sub" + data := model.ReportPropertyData{ + "b": model.ReportPropertyNode{ + Value: 111, + CreateTime: time.Now().Unix(), + }, + } + err := service.DevDataReport().Property(context.TODO(), deviceKey, data, subKey) + if err != nil { + t.Fatal(err) + } +} diff --git a/internal/logic/product/dev_device.go b/internal/logic/product/dev_device.go index f6ee256..e728b8c 100644 --- a/internal/logic/product/dev_device.go +++ b/internal/logic/product/dev_device.go @@ -3,21 +3,33 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "errors" + "fmt" + "sagooiot/api/v1/product" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/dcache" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/response" + "sagooiot/pkg/tsd" + "sagooiot/pkg/tsd/comm" + "sagooiot/pkg/utility" "strings" "time" + "github.com/xuri/excelize/v2" + "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" ) @@ -32,15 +44,23 @@ func deviceNew() *sDevDevice { return &sDevDevice{} } +// Get 获取设备详情 func (s *sDevDevice) Get(ctx context.Context, key string) (out *model.DeviceOutput, err error) { - err = dao.DevDevice.Ctx(ctx).WithAll().Where(dao.DevDevice.Columns().Key, key).Scan(&out) + err = dao.DevDevice.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: 0, + Name: consts.GetDetailDeviceOutput + key, + Force: false, + }).WithAll().Where(dao.DevDevice.Columns().Key, key).Scan(&out) if err != nil { return } if out == nil { - err = gerror.New("设备不存在") + err = errors.New("设备不存在") return } + if out.Status != 0 { + out.Status = dcache.GetDeviceStatus(ctx, out.Key) //查询设备状态 + } if out.Product != nil { out.ProductName = out.Product.Name if out.Product.Metadata != "" { @@ -50,15 +70,36 @@ func (s *sDevDevice) Get(ctx context.Context, key string) (out *model.DeviceOutp return } -func (s *sDevDevice) Detail(ctx context.Context, id uint) (out *model.DeviceOutput, err error) { - err = dao.DevDevice.Ctx(ctx).WithAll().Where(dao.DevDevice.Columns().Id, id).Scan(&out) +// GetAll 获取所有设备 +func (s *sDevDevice) GetAll(ctx context.Context) (out []*entity.DevDevice, err error) { + m := dao.DevDevice.Ctx(ctx) + err = m.Scan(&out) + return +} +func (s *sDevDevice) Detail(ctx context.Context, key string) (out *model.DeviceOutput, err error) { + err = dao.DevDevice.Ctx(ctx).WithAll().Where(dao.DevDevice.Columns().Key, key).Scan(&out) if err != nil { return } if out == nil { - err = gerror.New("设备不存在") + err = errors.New("设备不存在") return } + if out.Status != 0 { + out.Status = dcache.GetDeviceStatus(ctx, out.Key) //查询设备状态 + } + + //如果未设置,获取系统设置的默认超时时间 + if out.OnlineTimeout == 0 { + defaultTimeout, err := service.ConfigData().GetConfigByKey(ctx, consts.DeviceDefaultTimeoutTime) + if err != nil || defaultTimeout == nil { + defaultTimeout = &entity.SysConfig{ + ConfigValue: "30", + } + } + out.OnlineTimeout = gconv.Int(defaultTimeout.ConfigValue) + } + if out.Product != nil { out.ProductName = out.Product.Name if out.Product.Metadata != "" { @@ -74,13 +115,28 @@ func (s *sDevDevice) ListForPage(ctx context.Context, in *model.ListDeviceForPag m := dao.DevDevice.Ctx(ctx).OrderDesc(c.Id) if in.Status != "" { - m = m.Where(c.Status+" = ", gconv.Int(in.Status)) + //获取当前在线的设备KEY列表 + onlineDevice, err := dcache.GetOnlineDeviceList() + if in.Status == gconv.String(consts.DeviceStatueOnline) { + if err == nil && len(onlineDevice) > 0 { + m = m.Where(c.Key, onlineDevice) + } else { + return nil, nil + } + + } else if in.Status == gconv.String(consts.DeviceStatueOffline) { + if err == nil && len(onlineDevice) > 0 { + m = m.WhereNotIn(c.Key, onlineDevice) + } + } else { + m = m.Where(c.Status, gconv.Int(in.Status)) + } } if in.Key != "" { m = m.Where(c.Key, in.Key) } - if in.ProductId > 0 { - m = m.Where(c.ProductId, in.ProductId) + if in.ProductKey != "" { + m = m.Where(c.ProductKey, in.ProductKey) } if in.TunnelId > 0 { m = m.Where(c.TunnelId, in.TunnelId) @@ -92,6 +148,11 @@ func (s *sDevDevice) ListForPage(ctx context.Context, in *model.ListDeviceForPag m = m.WhereBetween(c.CreatedAt, in.DateRange[0], in.DateRange[1]) } + m = m.WhereIn( + dao.DevDevice.Columns().ProductKey, + dao.DevProduct.Ctx(ctx).Fields(dao.DevProduct.Columns().Key), + ) + out.Total, _ = m.Count() out.CurrentPage = in.PageNum err = m.WithAll().Page(in.PageNum, in.PageSize).Scan(&out.Device) @@ -103,21 +164,29 @@ func (s *sDevDevice) ListForPage(ctx context.Context, in *model.ListDeviceForPag if v.Product != nil { out.Device[i].ProductName = v.Product.Name } + if out.Device[i].Status != 0 { + out.Device[i].Status = dcache.GetDeviceStatus(ctx, out.Device[i].Key) + } + out.Device[i].Product.Metadata = "" } return } // List 已发布产品的设备列表 -func (s *sDevDevice) List(ctx context.Context, in *model.ListDeviceInput) (list []*model.DeviceOutput, err error) { +func (s *sDevDevice) List(ctx context.Context, productKey string, keyWord string) (list []*model.DeviceOutput, err error) { m := dao.DevDevice.Ctx(ctx). Where(dao.DevDevice.Columns().Status+" > ?", model.DeviceStatusNoEnable) - if in.ProductId > 0 { - m = m.Where(dao.DevDevice.Columns().ProductId, in.ProductId) + if productKey != "" { + m = m.Where(dao.DevDevice.Columns().ProductKey, productKey) + } + if keyWord != "" { + m = m.WhereLike(dao.DevDevice.Columns().Key, "%"+keyWord+"%").WhereOrLike(dao.DevDevice.Columns().Name, "%"+keyWord+"%") } - err = m.WhereIn(dao.DevDevice.Columns().ProductId, + + err = m.WhereIn(dao.DevDevice.Columns().ProductKey, dao.DevProduct.Ctx(ctx). - Fields(dao.DevProduct.Columns().Id). + Fields(dao.DevProduct.Columns().Key). Where(dao.DevProduct.Columns().Status, model.ProductStatusOn)). WithAll(). OrderDesc(dao.DevDevice.Columns().Id). @@ -141,7 +210,76 @@ func (s *sDevDevice) Add(ctx context.Context, in *model.AddDeviceInput) (deviceI Where(dao.DevDevice.Columns().Key, in.Key). Value() if id.Uint() > 0 { - err = gerror.New("设备标识重复") + err = errors.New("设备标识重复") + return + } + + // 设备标识不能和产品标识重复 + pout, err := service.DevProduct().Detail(ctx, in.Key) + if err != nil { + return + } + if pout != nil { + err = errors.New("设备标识不能和产品标识重复") + return + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + rs, err := dao.DevDevice.Ctx(ctx).Data(do.DevDevice{ + DeptId: service.Context().GetUserDeptId(ctx), + Key: in.Key, + Name: in.Name, + ProductKey: in.ProductKey, + Desc: in.Desc, + Version: in.Version, + Lng: in.Lng, + Lat: in.Lat, + AuthType: in.AuthType, + AuthUser: in.AuthUser, + AuthPasswd: in.AuthPasswd, + AccessToken: in.AccessToken, + CertificateId: in.CertificateId, + //ExtensionInfo: in.ExtensionInfo, + Status: 0, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + //Address: in.Address, + }).Insert() + if err != nil { + return + } + //北向设备添加消息 + north.WriteMessage(ctx, north.DeviceAddMessageTopic, nil, in.ProductKey, in.Key, iotModel.DeviceAddMessage{ + Timestamp: time.Now().UnixMilli(), + Desc: "", + }) + newId, err := rs.LastInsertId() + deviceId = uint(newId) + + // 设备标签 + if len(in.Tags) > 0 { + for _, v := range in.Tags { + intag := model.AddTagDeviceInput{ + DeviceId: deviceId, + DeviceKey: in.Key, + Key: v.Key, + Name: v.Name, + Value: v.Value, + } + if err = service.DevDeviceTag().Add(ctx, &intag); err != nil { + return + } + } + } + + return +} + +func (s *sDevDevice) Edit(ctx context.Context, in *model.EditDeviceInput) (err error) { + out, err := s.Detail(ctx, in.Key) + if err != nil { return } @@ -153,26 +291,129 @@ func (s *sDevDevice) Add(ctx context.Context, in *model.AddDeviceInput) (deviceI if err != nil { return } - param.CreateBy = uint(loginUserId) - param.Status = 0 + param.UpdatedBy = uint(loginUserId) + param.Id = nil - rs, err := dao.DevDevice.Ctx(ctx).Data(param).Insert() + _, err = dao.DevDevice.Ctx(ctx).Data(param).Where(dao.DevDevice.Columns().Key, in.Key).Update() if err != nil { return } - newId, err := rs.LastInsertId() - deviceId = uint(newId) + + // 设备标签 + if len(in.Tags) > 0 { + var l []model.AddTagDeviceInput + for _, v := range in.Tags { + intag := model.AddTagDeviceInput{ + DeviceId: out.Id, + DeviceKey: out.Key, + Key: v.Key, + Name: v.Name, + Value: v.Value, + } + l = append(l, intag) + } + if err = service.DevDeviceTag().Update(ctx, out.Id, l); err != nil { + return + } + } + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailDeviceOutput+out.Key) return } -func (s *sDevDevice) Edit(ctx context.Context, in *model.EditDeviceInput) (err error) { - total, err := dao.DevDevice.Ctx(ctx).Where(dao.DevDevice.Columns().Id, in.Id).Count() +// UpdateDeviceStatusInfo 更新设备状态信息,设备上线、离线、注册 +func (s *sDevDevice) UpdateDeviceStatusInfo(ctx context.Context, deviceKey string, status int, timestamp time.Time) (err error) { + device, err := s.Get(ctx, deviceKey) if err != nil { return } - if total == 0 { - return gerror.New("设备不存在") + if device == nil { + err = errors.New("设备不存在") + return + } + var data = g.Map{} + switch status { + case consts.DeviceStatueOnline: + if device.RegistryTime == nil { + data[dao.DevDevice.Columns().RegistryTime] = timestamp + } else { + + data[dao.DevDevice.Columns().LastOnlineTime] = timestamp + } + data[dao.DevDevice.Columns().Status] = consts.DeviceStatueOnline + + case consts.DeviceStatueOffline: + data[dao.DevDevice.Columns().LastOnlineTime] = timestamp + data[dao.DevDevice.Columns().Status] = consts.DeviceStatueOffline + } + _, err = dao.DevDevice.Ctx(ctx). + Data(data). + Where(dao.DevDevice.Columns().Key, deviceKey). + Update() + return +} + +// BatchUpdateDeviceStatusInfo 批量更新设备状态信息,设备上线、离线、注册 +func (s *sDevDevice) BatchUpdateDeviceStatusInfo(ctx context.Context, deviceStatusLogList []iotModel.DeviceStatusLog) (err error) { + + onLineData := g.Map{} + onlineDeviceKeyList := make([]string, 0) + + offLineData := g.Map{} + offLineDeviceKeyList := make([]string, 0) + + registryData := g.Map{} + registryDeviceKeyList := make([]string, 0) + + for _, statusLog := range deviceStatusLogList { + switch statusLog.Status { + case consts.DeviceStatueOnline: + device, err := dcache.GetDeviceDetailInfo(statusLog.DeviceKey) + if err != nil { + continue + } + + if device.RegistryTime == nil { + registryData[dao.DevDevice.Columns().RegistryTime] = statusLog.Timestamp + registryDeviceKeyList = append(registryDeviceKeyList, statusLog.DeviceKey) + } + + onLineData[dao.DevDevice.Columns().LastOnlineTime] = statusLog.Timestamp + onLineData[dao.DevDevice.Columns().Status] = consts.DeviceStatueOnline + onlineDeviceKeyList = append(onlineDeviceKeyList, statusLog.DeviceKey) + case consts.DeviceStatueOffline: + offLineData[dao.DevDevice.Columns().LastOnlineTime] = statusLog.Timestamp + offLineData[dao.DevDevice.Columns().Status] = consts.DeviceStatueOffline + offLineDeviceKeyList = append(offLineDeviceKeyList, statusLog.DeviceKey) + } + } + if len(registryDeviceKeyList) > 0 { + _, err = dao.DevDevice.Ctx(ctx). + Data(registryData). + WhereIn(dao.DevDevice.Columns().Key, registryDeviceKeyList). + Update() + } + if len(onlineDeviceKeyList) > 0 { + _, err = dao.DevDevice.Ctx(ctx). + Data(onLineData). + WhereIn(dao.DevDevice.Columns().Key, onlineDeviceKeyList). + Update() + } + if len(offLineDeviceKeyList) > 0 { + _, err = dao.DevDevice.Ctx(ctx). + Data(offLineData). + WhereIn(dao.DevDevice.Columns().Key, onlineDeviceKeyList). + Update() + } + + return +} + +func (s *sDevDevice) UpdateExtend(ctx context.Context, in *model.DeviceExtendInput) (err error) { + _, err = s.Detail(ctx, in.Key) + if err != nil { + return } //获取当前登录用户ID @@ -183,39 +424,42 @@ func (s *sDevDevice) Edit(ctx context.Context, in *model.EditDeviceInput) (err e if err != nil { return } - param.UpdateBy = uint(loginUserId) + param.UpdatedBy = uint(loginUserId) param.Id = nil - _, err = dao.DevDevice.Ctx(ctx).Data(param).Where(dao.DevDevice.Columns().Id, in.Id).Update() + _, err = dao.DevDevice.Ctx(ctx).Data(param).Where(dao.DevDevice.Columns().Key, in.Key).Update() + return } -func (s *sDevDevice) Del(ctx context.Context, ids []uint) (err error) { +func (s *sDevDevice) Del(ctx context.Context, keys []string) (err error) { var p []*entity.DevDevice - err = dao.DevDevice.Ctx(ctx).WhereIn(dao.DevDevice.Columns().Id, ids).Scan(&p) + err = dao.DevDevice.Ctx(ctx).WhereIn(dao.DevDevice.Columns().Key, keys).Scan(&p) if err != nil { return } if len(p) == 0 { - return gerror.New("设备不存在") + return errors.New("设备不存在") } - if len(p) == 1 && p[0].Status > model.DeviceStatusNoEnable { - return gerror.New("设备已启用,不能删除") + + // 状态校验 + for _, v := range p { + if v.Status > model.DeviceStatusNoEnable { + return errors.New(fmt.Sprintf("设备(%s)已启用,不能删除", v.Key)) + } } //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - - for _, id := range ids { - res, _ := s.Detail(ctx, id) - + for _, key := range keys { + res, _ := s.Detail(ctx, key) rs, err := dao.DevDevice.Ctx(ctx). Data(do.DevDevice{ DeletedBy: uint(loginUserId), DeletedAt: gtime.Now(), }). Where(dao.DevDevice.Columns().Status, model.DeviceStatusNoEnable). - Where(dao.DevDevice.Columns().Id, id). + Where(dao.DevDevice.Columns().Key, key). Unscoped(). Update() if err != nil { @@ -229,54 +473,123 @@ func (s *sDevDevice) Del(ctx context.Context, ids []uint) (err error) { if err != nil { return err } + + // 删除网关子设备TD子表 + if res.Product.DeviceType == model.DeviceTypeGateway { + subList, err := s.bindList(ctx, res.Key) + if err != nil { + return err + } + for _, sub := range subList { + if sub.MetadataTable == 1 { + // 删除TD子表 + err = service.TSLTable().DropTable(ctx, sub.Key) + if err != nil { + return err + } + } + } + } } + //北向设备删除消息 + north.WriteMessage(ctx, north.DeviceDeleteMessageTopic, nil, res.Product.Key, res.Key, iotModel.DeviceDeleteMessage{ + Timestamp: time.Now().UnixMilli(), + Desc: "", + }) } return } // Deploy 设备启用 -func (s *sDevDevice) Deploy(ctx context.Context, id uint) (err error) { - out, err := s.Detail(ctx, id) +func (s *sDevDevice) Deploy(ctx context.Context, key string) (err error) { + //获取设备信息 + device, err := s.Detail(ctx, key) if err != nil { return } - if out.Status > model.DeviceStatusNoEnable { - return gerror.New("设备已启用") + if device.Status > model.DeviceStatusNoEnable { + return } - pd, err := service.DevProduct().Detail(ctx, out.ProductId) + pd, err := service.DevProduct().Detail(ctx, device.ProductKey) if err != nil { return err } if pd == nil { - return gerror.New("产品不存在") + return errors.New("产品不存在") } if pd.Status != model.ProductStatusOn { - return gerror.New("产品未发布,请先发布产品") + return errors.New("产品未发布,请先发布产品") } - err = dao.DevDevice.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevDevice.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + // 是否创建TD表结构 + var isCreate bool + + if device.MetadataTable == 0 { + isCreate = true + } + // 检查TD表是否存在,不存在,则补建 + if device.MetadataTable == 1 { + table := comm.DeviceTableName(device.Key) + b, _ := service.TSLTable().CheckTable(ctx, table) + if err != nil { + if err.Error() == "sql: no rows in result set" { + isCreate = true + } else { + return err + } + } + if !b { + isCreate = true + } + } + + // 创建TD表结构 + if isCreate { + err = service.TSLTable().CreateTable(ctx, pd.Key, device.Key) + if err != nil { + return err + } + } + + // 更新状态 _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().Status: model.DeviceStatusOff}). - Where(dao.DevDevice.Columns().Id, id). + Data(g.Map{ + dao.DevDevice.Columns().Status: model.DeviceStatusOff, + dao.DevDevice.Columns().MetadataTable: 1, + }). + Where(dao.DevDevice.Columns().Key, key). Update() + + //设备启用后,更新缓存数据 + device.Status = model.DeviceStatusOff + err = dcache.SetDeviceDetailInfo(device.Key, device) if err != nil { - return err + g.Log().Debug(ctx, "Deploy 设备数据存入缓存失败", err.Error()) } - // 建立TD子表 - if out.MetadataTable == 0 { - err = service.TSLTable().CreateTable(ctx, out.Product.Key, out.Key) + // 网关启用子设备 + if pd.DeviceType == model.DeviceTypeGateway { + subList, err := s.bindList(ctx, device.Key) if err != nil { return err } - _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().MetadataTable: 1}). - Where(dao.DevDevice.Columns().Id, id). - Update() + for _, sub := range subList { + if err := s.Deploy(ctx, sub.Key); err != nil { + return err + } + //设备启用后,更新缓存数据 + sub.Status = model.DeviceStatusOff + err = dcache.SetDeviceDetailInfo(sub.Key, sub) + if err != nil { + g.Log().Debug(ctx, "Deploy 子设备数据存入缓存失败", err.Error()) + } + } } + return err }) @@ -284,338 +597,375 @@ func (s *sDevDevice) Deploy(ctx context.Context, id uint) (err error) { } // Undeploy 设备禁用 -func (s *sDevDevice) Undeploy(ctx context.Context, id uint) (err error) { - var p *entity.DevDevice - - err = dao.DevDevice.Ctx(ctx).Where(dao.DevDevice.Columns().Id, id).Scan(&p) +func (s *sDevDevice) Undeploy(ctx context.Context, key string) (err error) { + //获取设备信息 + device, err := s.Detail(ctx, key) if err != nil { return } - if p == nil { - return gerror.New("设备不存在") - } - if p.Status == model.DeviceStatusNoEnable { - return gerror.New("设备已禁用") - } - - _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().Status: model.DeviceStatusNoEnable}). - Where(dao.DevDevice.Columns().Id, id). - Update() - - return -} - -// Online 设备上线 -func (s *sDevDevice) Online(ctx context.Context, key string) (err error) { - out, err := s.Get(ctx, key) - if err != nil { + if device.Status == model.DeviceStatusNoEnable { return } - if out.Status == model.DeviceStatusOn { - return gerror.New("设备已上线") - } - err = dao.DevDevice.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevDevice.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().Status: model.DeviceStatusOn}). - Where(dao.DevDevice.Columns().Id, out.Id). + Data(g.Map{dao.DevDevice.Columns().Status: model.DeviceStatusNoEnable}). + Where(dao.DevDevice.Columns().Key, key). Update() if err != nil { return err } - // 建立TD子表 - if out.MetadataTable == 0 { - err = service.TSLTable().CreateTable(ctx, out.Product.Key, out.Key) + //设备停用后,更新缓存数据 + device.Status = model.DeviceStatusNoEnable + err = dcache.SetDeviceDetailInfo(device.Key, device) + if err != nil { + g.Log().Debug(ctx, "Deploy 设备数据存入缓存失败", err.Error()) + } + + // 网关禁用子设备 + if device.Product.DeviceType == model.DeviceTypeGateway { + subList, err := s.bindList(ctx, device.Key) if err != nil { return err } - _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().MetadataTable: 1}). - Where(dao.DevDevice.Columns().Id, out.Id). - Update() + for _, sub := range subList { + if err := s.Undeploy(ctx, sub.Key); err != nil { + return err + } + //设备停用后,更新缓存数据 + sub.Status = model.DeviceStatusNoEnable + err = dcache.SetDeviceDetailInfo(sub.Key, sub) + if err != nil { + g.Log().Debug(ctx, "Deploy 子设备数据存入缓存失败", err.Error()) + } + } } + return err }) return } -// Offline 设备下线 -func (s *sDevDevice) Offline(ctx context.Context, key string) (err error) { - out, err := s.Get(ctx, key) - if err != nil { - return - } - if out.Status == model.DeviceStatusOff { - return gerror.New("设备已离线") - } - - _, err = dao.DevDevice.Ctx(ctx). - Data(g.Map{dao.DevDevice.Columns().Status: model.DeviceStatusOff}). - Where(dao.DevDevice.Columns().Id, out.Id). - Update() - - return -} - -// 统计产品下的设备数量 -func (s *sDevDevice) TotalByProductId(ctx context.Context, productIds []uint) (totals map[uint]int, err error) { - r, err := dao.DevDevice.Ctx(ctx).Fields(dao.DevDevice.Columns().ProductId+", count(*) as total"). - WhereIn(dao.DevDevice.Columns().ProductId, productIds). - Group(dao.DevDevice.Columns().ProductId). +// TotalByProductKey 统计产品下的设备数量 +func (s *sDevDevice) TotalByProductKey(ctx context.Context, productKeys []string) (totals map[string]int, err error) { + m := dao.DevDevice.Ctx(ctx) + r, err := m.Fields(dao.DevDevice.Columns().ProductKey+", count(*) as total"). + WhereIn(dao.DevDevice.Columns().ProductKey, productKeys). + Group(dao.DevDevice.Columns().ProductKey). All() if err != nil || r.Len() == 0 { return } - totals = make(map[uint]int, r.Len()) + totals = make(map[string]int, r.Len()) for _, v := range r { t := gconv.Int(v["total"]) - id := gconv.Uint(v[dao.DevDevice.Columns().ProductId]) - totals[id] = t + productKey := gconv.String(v[dao.DevDevice.Columns().ProductKey]) + totals[productKey] = t } - for _, id := range productIds { - if _, ok := totals[id]; !ok { - totals[id] = 0 + for _, key := range productKeys { + if _, ok := totals[key]; !ok { + totals[key] = 0 } } return } -// Total 统计设备数量 -func (s *sDevDevice) Total(ctx context.Context) (data model.DeviceTotalOutput, err error) { - key := consts.IotOverviewIndex - tag := "device" - value := common.Cache().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { - var rs model.DeviceTotalOutput - // 设备总量 - rs.DeviceTotal, err = dao.DevDevice.Ctx(ctx).Count() - if err != nil { - return - } - - // 离线设备数量 - rs.DeviceOffline, err = dao.DevDevice.Ctx(ctx).Where(dao.DevDevice.Columns().Status, model.DeviceStatusOff).Count() - if err != nil { - return - } +// getTotalForMonthsData 获取统计设备月度消息数量 +func (s *sDevDevice) getTotalForMonthsData(ctx context.Context) (data map[int]int, err error) { - // 产品总量 - rs.ProductTotal, err = dao.DevProduct.Ctx(ctx).Count() - if err != nil { - return + data = make(map[int]int, 12) + for i := 0; i < 12; i++ { + data[i+1] = 0 + } + devices, err := s.GetAll(ctx) + if err != nil { + return + } + if devices != nil { + var deviceKeys []string + for _, device := range devices { + deviceKeys = append(deviceKeys, "'"+device.Key+"'") } - // 产品新增数量 - rs.ProductAdded, err = dao.DevProduct.Ctx(ctx). - Where(dao.DevProduct.Columns().CreatedAt+">=?", gtime.Now().Format("Y-m-d")). - Count() - if err != nil { - return - } + tsdDb := tsd.DB() + defer tsdDb.Close() // 设备消息总量 TDengine - sql := "select count(*) as num from device_log" - data, err := service.TdEngine().GetOne(ctx, sql) + var list gdb.Result + sql := "select substr(to_iso8601(ts), 1, 7) as ym, count(*) as num from device_log where device in (?) and ts >= '?' group by substr(to_iso8601(ts), 1, 7)" + list, err = tsdDb.GetTableDataAll(ctx, sql, strings.Join(deviceKeys, ","), gtime.Now().Format("Y-01-01 00:00:00")) if err != nil { return } - rs.MsgTotal = data["num"].Int() - // 设备消息新增数量 TDengine - sql = "select count(*) as num from device_log where ts >= '?'" - data, err = service.TdEngine().GetOne(ctx, sql, gtime.Now().Format("Y-m-d")) - if err != nil { - return + for _, v := range list { + m := gstr.SubStr(v["ym"].String(), 5) + data[gconv.Int(m)] = v["num"].Int() } - rs.MsgAdded = data["num"].Int() + } + return +} - // 设备报警总量 - rs.AlarmTotal, err = dao.AlarmLog.Ctx(ctx).Count() - if err != nil { - return - } +// getTotalForDayData 统计设备最近一月消息数量 +func (s *sDevDevice) getTotalForDayData(ctx context.Context) (data map[string]int, err error) { - // 设备报警增量 - rs.AlarmAdded, err = dao.AlarmLog.Ctx(ctx). - Where(dao.AlarmLog.Columns().CreatedAt+">=?", gtime.Now().Format("Y-m-d")). - Count() - if err != nil { - return - } + start := time.Now().AddDate(0, -1, 1) + data = make(map[string]int) + for i := start; i.Before(time.Now()); { + data[i.Format("2006-01-02")] = 0 + i = i.Add(24 * time.Hour) + } - value = rs + devices, err := s.GetAll(ctx) + if err != nil { return - }, 7200*time.Second, tag) - - err = gconv.Struct(value, &data) - return -} - -// 统计设备月度数量 -func (s *sDevDevice) TotalForMonths(ctx context.Context) (data map[int]int, err error) { - key := "device:totalForMonths" - tag := "device" - value := common.Cache().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { - rs := make(map[int]int, 12) - for i := 0; i < 12; i++ { - rs[i+1] = 0 + } + if devices != nil { + var deviceKeys []string + for _, device := range devices { + deviceKeys = append(deviceKeys, "'"+device.Key+"'") } + tsdDb := tsd.DB() + defer tsdDb.Close() + // 设备消息总量 TDengine - sql := "select substr(to_iso8601(ts), 1, 7) as ym, count(*) as num from device_log where ts >= '?' group by substr(to_iso8601(ts), 1, 7)" - list, err := service.TdEngine().GetAll(ctx, sql, gtime.Now().Format("Y-01-01 00:00:00")) + var list gdb.Result + sql := "select substr(to_iso8601(ts), 1, 10) as ymd, count(*) as num from device_log where device in (?) partition by substr(to_iso8601(ts), 1, 10) interval(4w)" + list, err = tsdDb.GetTableDataAll(ctx, sql, strings.Join(deviceKeys, ",")) if err != nil { return } for _, v := range list { - m := gstr.SubStr(v["ym"].String(), 5) - rs[gconv.Int(m)] = v["num"].Int() + data[v["ymd"].String()] = v["num"].Int() } + } + + return +} + +// getAlarmTotalForMonthsData 统计设备月度告警数量 +func (s *sDevDevice) getAlarmTotalForMonthsData(ctx context.Context) (data map[int]int, err error) { + + data = make(map[int]int, 12) + for i := 0; i < 12; i++ { + data[i+1] = 0 + } + + devices, err := s.GetAll(ctx) + if err != nil { + return + } + var deviceKeys []string + for _, device := range devices { + deviceKeys = append(deviceKeys, device.Key) + } - value = rs + at := dao.AlarmLog.Columns().CreatedAt + list, err := dao.AlarmLog.Ctx(ctx). + Fields("date_format("+at+", '%Y%m') as ym, count(*) as num"). + Where(at+">=?", gtime.Now().Format("Y-01-01 00:00:00")). + WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + Group("date_format(" + at + ", '%Y%m')"). + All() + if err != nil { return - }, 7200*time.Second, tag) + } + + for _, v := range list { + m := gstr.SubStr(v["ym"].String(), 4) + data[gconv.Int(m)] = v["num"].Int() + } - err = gconv.Scan(value, &data) return } -// 统计设备月度告警数量 -func (s *sDevDevice) AlarmTotalForMonths(ctx context.Context) (data map[int]int, err error) { - key := "device:alarmTotalForMonths" - tag := "device" - value := common.Cache().GetOrSetFunc(ctx, key, func(ctx context.Context) (value interface{}, err error) { - rs := make(map[int]int, 12) - for i := 0; i < 12; i++ { - rs[i+1] = 0 - } +// getAlarmTotalForDayData 获取统计设备最近一个月告警数量 +func (s *sDevDevice) getAlarmTotalForDayData(ctx context.Context) (data map[string]int, err error) { - at := dao.AlarmLog.Columns().CreatedAt - list, err := dao.AlarmLog.Ctx(ctx). - Fields("date_format("+at+", '%Y%m') as ym, count(*) as num"). - Where(at+">=?", gtime.Now().Format("Y-01-01 00:00:00")). - Group("date_format(" + at + ", '%Y%m')"). - All() - if err != nil { - return - } + start := time.Now().AddDate(0, -1, 1) + data = make(map[string]int) + for i := start; i.Before(time.Now()); { + data[i.Format("2006-01-02")] = 0 + i = i.Add(24 * time.Hour) + } - for _, v := range list { - m := gstr.SubStr(v["ym"].String(), 4) - rs[gconv.Int(m)] = v["num"].Int() - } + devices, err := s.GetAll(ctx) + if err != nil { + return + } + var deviceKeys []string + for _, device := range devices { + deviceKeys = append(deviceKeys, "'"+device.Key+"'") + } - value = rs + at := dao.AlarmLog.Columns().CreatedAt + list, err := dao.AlarmLog.Ctx(ctx). + Fields("date_format("+at+", '%Y-%m-%d') as ymd, count(*) as num"). + Where(at+">=?", start.Format("20060102")). + WhereIn(dao.AlarmLog.Columns().DeviceKey, deviceKeys). + Group("date_format(" + at + ", '%Y-%m-%d')"). + All() + if err != nil { return - }, 7200*time.Second, tag) + } + + for _, v := range list { + data[v["ymd"].String()] = v["num"].Int() + } - err = gconv.Scan(value, &data) return } // RunStatus 运行状态 -func (s *sDevDevice) RunStatus(ctx context.Context, id uint) (out *model.DeviceRunStatusOutput, err error) { - p, err := s.Detail(ctx, id) +func (s *sDevDevice) RunStatus(ctx context.Context, deviceKey string) (out *model.DeviceRunStatusOutput, err error) { + + device, err := dcache.GetDeviceDetailInfo(deviceKey) if err != nil { - return + return nil, errors.New("设备不存在") } - out = new(model.DeviceRunStatusOutput) - out.Status = p.Status - // out.LastOnlineTime = p.LastOnlineTime + out.Status = dcache.GetDeviceStatus(ctx, deviceKey) - if p.Status == model.DeviceStatusNoEnable { + //获取数据示例 + deviceValueList := dcache.GetDeviceDetailData(context.Background(), deviceKey) + if len(deviceValueList) == 0 { return } - // 属性值获取 - sql := "select * from ? order by ts desc limit 1" - rs, err := service.TdEngine().GetOne(ctx, sql, p.Key) - if err != nil { - return + for _, d := range deviceValueList[0] { + out.LastOnlineTime = gtime.New(d.CreateTime) } - out.LastOnlineTime = rs["ts"].GTime() var properties []model.DevicePropertiy - for _, v := range p.TSL.Properties { - // 获取当天属性值列表 - var ls gdb.Result - if _, ok := rs[strings.ToLower(v.Key)]; ok { - sql := "select ? from ? where ts >= '?' and ? is not null order by ts desc" - ls, _ = service.TdEngine().GetAll(ctx, sql, strings.ToLower(v.Key), p.Key, gtime.Now().Format("Y-m-d"), strings.ToLower(v.Key)) - } - + for _, v := range device.TSL.Properties { unit := "" if v.ValueType.Unit != nil { unit = *v.ValueType.Unit } - value := rs[strings.ToLower(v.Key)] - if value.IsEmpty() && ls.Len() > 0 { - value = ls.Array(strings.ToLower(v.Key))[ls.Len()-1] + var valueList = make([]*g.Var, 0) + for _, d := range deviceValueList { + valueList = append(valueList, g.NewVar(d[v.Key].Value)) } + pro := model.DevicePropertiy{ Key: v.Key, Name: v.Name, Type: v.ValueType.Type, Unit: unit, - Value: value, - List: ls.Array(strings.ToLower(v.Key)), + Value: valueList[0], + List: valueList, } properties = append(properties, pro) } out.Properties = properties - return } -// GetProperty 获取指定属性值 -func (s *sDevDevice) GetProperty(ctx context.Context, in *model.DeviceGetPropertyInput) (out *model.DevicePropertiy, err error) { - p, err := s.Detail(ctx, in.Id) +// GetLatestProperty 获取设备最新的属性值 +func (s *sDevDevice) GetLatestProperty(ctx context.Context, key string) (list []model.DeviceLatestProperty, err error) { + p, err := s.Get(ctx, key) if err != nil { return } if p.Status == model.DeviceStatusNoEnable { - err = gerror.New("设备未启用") return } - sKey := in.PropertyKey - in.PropertyKey = strings.ToLower(in.PropertyKey) + deviceTable := comm.DeviceTableName(p.Key) - // 属性值获取 - sql := "select ? from ? where ? is not null order by ts desc limit 1" - rs, err := service.TdEngine().GetOne(ctx, sql, in.PropertyKey, p.Key, in.PropertyKey) - if err != nil { - return - } + tsdDb := tsd.DB() + defer tsdDb.Close() - var name string - var valueType string for _, v := range p.TSL.Properties { - if strings.ToLower(v.Key) == in.PropertyKey { - name = v.Name - valueType = v.ValueType.Type - break + ckey := comm.TsdColumnName(v.Key) + + // 获取属性最近有效值 + sql := "select ? from ? where ? is not null order by ts desc limit 1" + rs, err := tsdDb.GetTableDataOne(ctx, sql, ckey, deviceTable, ckey) + if err != nil { + return nil, err + } + value := rs[strings.ToLower(v.Key)] + if value.IsEmpty() { + continue } - } - out = new(model.DevicePropertiy) + unit := "" + if v.ValueType.Unit != nil { + unit = *v.ValueType.Unit + } + + pro := model.DeviceLatestProperty{ + Key: v.Key, + Name: v.Name, + Type: v.ValueType.Type, + Unit: unit, + Value: value, + } + list = append(list, pro) + } + return +} + +// GetProperty 获取指定属性值 +func (s *sDevDevice) GetProperty(ctx context.Context, in *model.DeviceGetPropertyInput) (out *model.DevicePropertiy, err error) { + p, err := s.Detail(ctx, in.DeviceKey) + if err != nil { + return + } + if p.Status == model.DeviceStatusNoEnable { + err = errors.New("设备未启用") + return + } + + tsdDb := tsd.DB() + defer tsdDb.Close() + + sKey := in.PropertyKey + in.PropertyKey = strings.ToLower(in.PropertyKey) + col := comm.TsdColumnName(in.PropertyKey) + + deviceTable := comm.DeviceTableName(p.Key) + + // 属性上报时间 + ctime := in.PropertyKey + "_time" + ctime = comm.TsdColumnName(ctime) + + // 属性值获取 + sql := "select ? from ? where ? is not null order by ? desc limit 1" + rs, err := tsdDb.GetTableDataOne(ctx, sql, col, deviceTable, col, ctime) + if err != nil { + return + } + + var name string + var valueType string + for _, v := range p.TSL.Properties { + if strings.ToLower(v.Key) == in.PropertyKey { + name = v.Name + valueType = v.ValueType.Type + break + } + } + + out = new(model.DevicePropertiy) out.Key = sKey out.Name = name out.Type = valueType out.Value = rs[in.PropertyKey] // 获取当天属性值列表 - sql = "select ? from ? where ts >= '?' and ? is not null order by ts desc" - ls, _ := service.TdEngine().GetAll(ctx, sql, in.PropertyKey, p.Key, gtime.Now().Format("Y-m-d"), in.PropertyKey) + sql = "select ? from ? where ? >= '?' order by ? desc" + ls, _ := tsdDb.GetTableDataAll(ctx, sql, col, deviceTable, ctime, gtime.Now().Format("Y-m-d"), ctime) out.List = ls.Array(in.PropertyKey) return @@ -623,49 +973,598 @@ func (s *sDevDevice) GetProperty(ctx context.Context, in *model.DeviceGetPropert // GetPropertyList 设备属性详情列表 func (s *sDevDevice) GetPropertyList(ctx context.Context, in *model.DeviceGetPropertyListInput) (out *model.DeviceGetPropertyListOutput, err error) { - p, err := s.Detail(ctx, in.Id) + resultList, total, currentPage := dcache.GetDeviceDetailDataByPage(ctx, in.DeviceKey, in.PageNum, in.PageSize) + if err != nil { + return + } + out = new(model.DeviceGetPropertyListOutput) + out.Total = total + out.CurrentPage = currentPage + + var pro []*model.DevicePropertiyOut + for _, d := range resultList { + pro = append(pro, &model.DevicePropertiyOut{ + Ts: gtime.New(d[in.PropertyKey].CreateTime), + Value: gvar.New(d[in.PropertyKey].Value), + }) + } + out.List = pro + + return +} + +// GetData 获取设备指定日期属性数据 +func (s *sDevDevice) GetData(ctx context.Context, in *model.DeviceGetDataInput) (list []model.DevicePropertiyOut, err error) { + p, err := s.Detail(ctx, in.DeviceKey) if err != nil { return } if p.Status == model.DeviceStatusNoEnable { - err = gerror.New("设备未启用") + err = errors.New("设备未启用") return } + deviceTable := comm.DeviceTableName(p.Key) + in.PropertyKey = strings.ToLower(in.PropertyKey) + col := comm.TsdColumnName(in.PropertyKey) - out = new(model.DeviceGetPropertyListOutput) + // 属性上报时间 + ctime := in.PropertyKey + "_time" + ctime = comm.TsdColumnName(ctime) + + where := fmt.Sprintf("%s >= %q", ctime, gtime.Now().Format("Y-m-d")) + if len(in.DateRange) > 1 { + where = fmt.Sprintf("%s >= %q and %s <= %q", ctime, in.DateRange[0], ctime, in.DateRange[1]) + } + + desc := "asc" + if in.IsDesc == 1 { + desc = "desc" + } + + tsdDb := tsd.DB() + defer tsdDb.Close() // TDengine - sql := "select count(*) as num from ? where ts >= '?' and ? is not null" - rs, err := service.TdEngine().GetOne(ctx, sql, p.Key, gtime.Now().Format("Y-m-d"), in.PropertyKey) + sql := "select ?, ? from ? where ? order by ? ?" + ls, _ := tsdDb.GetTableDataAll( + ctx, + sql, + fmt.Sprintf("distinct %s as ts", ctime), + col, + deviceTable, + where, + ctime, + desc, + ) + for _, v := range ls.List() { + list = append(list, model.DevicePropertiyOut{ + Ts: gvar.New(v["ts"]).GTime(), + Value: gvar.New(v[in.PropertyKey]), + }) + } + return +} + +// BindSubDevice 网关绑定子设备 +func (s *sDevDevice) BindSubDevice(ctx context.Context, in *model.DeviceBindInput) error { + gw, err := s.Get(ctx, in.GatewayKey) + if err != nil { + return err + } + if gw.Product.DeviceType != model.DeviceTypeGateway { + return errors.New("非网关,不能绑定子设备") + } + if len(in.SubKeys) == 0 { + return nil + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + for _, v := range in.SubKeys { + sub, err := s.Get(ctx, v) + if err != nil { + return err + } + if sub.Product.DeviceType != model.DeviceTypeSub { + return errors.New("非子设备类型,不能绑定") + } + + rs, err := dao.DevDeviceGateway.Ctx(ctx). + Where(dao.DevDeviceGateway.Columns().SubKey, v). + One() + if err != nil { + return err + } + if !rs.IsEmpty() { + return errors.New(fmt.Sprintf("%s,该子设备已被绑定", sub.Name)) + } + + _, err = dao.DevDeviceGateway.Ctx(ctx).Data(do.DevDeviceGateway{ + GatewayKey: in.GatewayKey, + SubKey: v, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() + if err != nil { + return err + } + } + return nil +} + +// UnBindSubDevice 网关解绑子设备 +func (s *sDevDevice) UnBindSubDevice(ctx context.Context, in *model.DeviceBindInput) error { + gw, err := s.Get(ctx, in.GatewayKey) + if err != nil { + return err + } + if gw.Product.DeviceType != model.DeviceTypeGateway { + return errors.New("非网关设备") + } + if len(in.SubKeys) == 0 { + return nil + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + _, err = dao.DevDeviceGateway.Ctx(ctx). + Data(do.DevDeviceGateway{ + DeletedBy: uint(loginUserId), + DeletedAt: gtime.Now(), + }). + Where(dao.DevDeviceGateway.Columns().GatewayKey, in.GatewayKey). + WhereIn(dao.DevDeviceGateway.Columns().SubKey, in.SubKeys). + Unscoped(). + Update() + return err +} + +// bindList 已绑定列表 +func (s *sDevDevice) bindList(ctx context.Context, gatewayKey string) (list []*model.DeviceOutput, err error) { + _, err = s.Get(ctx, gatewayKey) + if err != nil { + return + } + + var dgw []*entity.DevDeviceGateway + if err = dao.DevDeviceGateway.Ctx(ctx).Where(dao.DevDeviceGateway.Columns().GatewayKey, gatewayKey).Scan(&dgw); err != nil || len(dgw) == 0 { + return + } + + var subKeys []string + for _, v := range dgw { + subKeys = append(subKeys, v.SubKey) + } + + err = dao.DevDevice.Ctx(ctx). + WhereIn(dao.DevDevice.Columns().Key, subKeys). + WithAll(). + OrderDesc(dao.DevDevice.Columns().Id). + Scan(&list) + return +} + +// BindList 已绑定列表(分页) +func (s *sDevDevice) BindList(ctx context.Context, in *model.DeviceBindListInput) (out *model.DeviceBindListOutput, err error) { + _, err = s.Get(ctx, in.GatewayKey) if err != nil { return } + + var dgw []*entity.DevDeviceGateway + if err = dao.DevDeviceGateway.Ctx(ctx).Where(dao.DevDeviceGateway.Columns().GatewayKey, in.GatewayKey).Scan(&dgw); err != nil || len(dgw) == 0 { + return + } + + var subKeys []string + for _, v := range dgw { + subKeys = append(subKeys, v.SubKey) + } + + m := dao.DevDevice.Ctx(ctx). + WhereIn(dao.DevDevice.Columns().Key, subKeys). + WithAll(). + OrderDesc(dao.DevDevice.Columns().Id) + + out = &model.DeviceBindListOutput{} + out.Total, _ = m.Count() + out.CurrentPage = in.PageNum + err = m.Page(in.PageNum, in.PageSize).Scan(&out.List) + if err != nil { + return + } + + for i, v := range out.List { + out.List[i].Status = dcache.GetDeviceStatus(ctx, v.Key) + } + + return +} + +// ListForSub 子设备 +func (s *sDevDevice) ListForSub(ctx context.Context, in *model.ListForSubInput) (out *model.ListDeviceForPageOutput, err error) { + m := dao.DevDevice.Ctx(ctx) + if in.ProductKey != "" { + m = m.Where(dao.DevDevice.Columns().ProductKey, in.ProductKey) + } + if in.GatewayKey != "" { + rs, err := dao.DevDeviceGateway.Ctx(ctx). + Fields(dao.DevDeviceGateway.Columns().SubKey). + Where(dao.DevDeviceGateway.Columns().GatewayKey, in.GatewayKey). + Array() + if err == nil && len(rs) > 0 { + m = m.WhereNotIn(dao.DevDevice.Columns().Key, rs) + } + } + + m = m.WithAll().OrderDesc(dao.DevDevice.Columns().Id) + out = &model.ListDeviceForPageOutput{} + out.Total, _ = m.Count() + out.CurrentPage = in.PageNum + err = m.Page(in.PageNum, in.PageSize).Scan(&out.Device) + return +} + +// CheckBind 检查网关、子设备绑定关系 +func (s *sDevDevice) CheckBind(ctx context.Context, in *model.CheckBindInput) (bool, error) { + count, err := dao.DevDeviceGateway.Ctx(ctx). + Where(dao.DevDeviceGateway.Columns().GatewayKey, in.GatewayKey). + Where(dao.DevDeviceGateway.Columns().SubKey, in.SubKey). + Count() + if err != nil || count == 0 { + return false, err + } + return true, nil +} + +// DelSub 子设备删除 +func (s *sDevDevice) DelSub(ctx context.Context, key string) (err error) { + subDev, err := s.Detail(ctx, key) + if err != nil { + return + } + if subDev.Product.DeviceType != model.DeviceTypeSub { + return errors.New("该设备不是子设备类型") + } + if subDev.Status > model.DeviceStatusNoEnable { + return errors.New("设备已启用,不能删除") + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + now := gtime.Now() + + rs, err := dao.DevDevice.Ctx(ctx). + Data(do.DevDevice{ + DeletedBy: uint(loginUserId), + DeletedAt: now, + }). + Where(dao.DevDevice.Columns().Key, key). + Unscoped(). + Update() + if err != nil { + return err + } + + // 删除绑定关系 + _, err = dao.DevDeviceGateway.Ctx(ctx). + Data(do.DevDeviceGateway{ + DeletedBy: uint(loginUserId), + DeletedAt: now, + }). + Where(dao.DevDeviceGateway.Columns().SubKey, subDev.Key). + Unscoped(). + Update() + if err != nil { + return + } + + num, _ := rs.RowsAffected() + if num > 0 && subDev.MetadataTable == 1 { + // 删除TD子表 + err = service.TSLTable().DropTable(ctx, subDev.Key) + if err != nil { + return err + } + } + + return +} + +// AuthInfo 获取认证信息 +func (s *sDevDevice) AuthInfo(ctx context.Context, in *model.AuthInfoInput) (*model.AuthInfoOutput, error) { + if in.DeviceKey == "" && in.ProductKey == "" { + return nil, errors.New("缺少必要参数") + } + + out := &model.AuthInfoOutput{} + + if in.DeviceKey != "" { + device, err := s.Get(ctx, in.DeviceKey) + if err != nil { + return nil, err + } + out.AuthType = device.AuthType + out.AuthUser = device.AuthUser + out.AuthPasswd = device.AuthPasswd + out.AccessToken = device.AccessToken + out.CertificateId = device.CertificateId + + if out.AuthUser == "" && out.AuthPasswd == "" && out.AccessToken == "" && out.CertificateId == 0 { + in.ProductKey = device.Product.Key + } else { + in.ProductKey = "" + } + } + + if in.ProductKey != "" { + productData, err := service.DevProduct().Detail(ctx, in.ProductKey) + if err != nil { + return nil, err + } + out.AuthType = productData.AuthType + out.AuthUser = productData.AuthUser + out.AuthPasswd = productData.AuthPasswd + out.AccessToken = productData.AccessToken + out.CertificateId = productData.CertificateId + } + + if out.CertificateId > 0 { + if err := dao.SysCertificate.Ctx(ctx).Where(dao.SysCertificate.Columns().Id, out.CertificateId).Scan(&out.Certificate); err != nil { + return nil, err + } + } + + return out, nil +} + +// GetDeviceOnlineTimeOut 获取设备在线超时时长 +func (s *sDevDevice) GetDeviceOnlineTimeOut(ctx context.Context, deviceKey string) (timeOut int) { + // 获取设备在线超时时长 + timeOut = consts.DeviceOnlineTimeOut + tv, err := dao.DevDevice.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: time.Second * 5, + Name: "DeviceOnlineTimeOut" + deviceKey, + Force: false, + }).Where(dao.DevDevice.Columns().Key, deviceKey).Fields(dao.DevDevice.Columns().OnlineTimeout).Value() + if err == nil && tv.Int() > 0 { + timeOut = tv.Int() + } + return +} + +// ExportDevices 导出设备 +func (s *sDevDevice) ExportDevices(ctx context.Context, req *product.ExportDevicesReq) (res product.ExportDevicesRes, err error) { + var data []model.DeviceOutput + m := dao.DevDevice.Ctx(ctx).WithAll().Where(dao.DevDevice.Columns().ProductKey, req.ProductKey) + if err = m.Scan(&data); err != nil { + return + } + + var outList []*model.DeviceExport + for _, v := range data { + var status string + switch v.DevDevice.Status { + case model.DeviceStatusNoEnable: + status = "未启用" + case model.DeviceStatusOff: + status = "离线" + case model.DeviceStatusOn: + status = "在线" + } + var reqData = new(model.DeviceExport) + reqData.Desc = v.DevDevice.Desc + reqData.DeviceKey = v.DevDevice.Key + reqData.DeviceName = v.DevDevice.Name + reqData.ProductName = v.Product.Name + reqData.Version = v.DevDevice.Version + reqData.DeviceType = v.Product.DeviceType + reqData.Status = status + outList = append(outList, reqData) + } + if len(outList) == 0 { + var reqData = new(model.DeviceExport) + outList = append(outList, reqData) + } + + //处理数据并导出 + var outData []interface{} + for _, d := range outList { + outData = append(outData, d) + } + dataRes := utility.ToExcel(outData) + var request = g.RequestFromCtx(ctx) + response.ToXls(request, dataRes, "设备列表") + + return +} + +// ImportDevices 导入设备 +func (s *sDevDevice) ImportDevices(ctx context.Context, req *product.ImportDevicesReq) (res product.ImportDevicesRes, err error) { + file, err := req.File.Open() + if err != nil { + return + } + xlsx, err := excelize.OpenReader(file) + + if err != nil { + return + } + + rows, err := xlsx.GetRows("Sheet1") + if err != nil { + return + } + device := new(entity.DevDevice) + if len(rows) < 2 { + err = errors.New("请添加设备") + return + } + productData := new(entity.DevProduct) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, req.ProductKey).Scan(&productData) + if err != nil { + err = errors.New("产品不存在") + return + } + for rIndex, row := range rows { + if len(row) < 7 { + continue + } + if rIndex == 0 { + continue + } + bl := gregex.IsMatch("^[A-Za-z_]+[A-Za-z0-9_]*|[0-9]+$", []byte(row[1])) + if !bl { + res.Fail++ + res.DevicesKey = append(res.DevicesKey, row[1]) + continue + } + device.DeptId = service.Context().GetUserDeptId(ctx) + device.ProductKey = productData.Key + device.Name = row[0] + device.Key = row[1] + device.Desc = row[2] + device.Version = row[3] + device.Lng = row[4] + device.Lat = row[5] + device.OnlineTimeout = gconv.Int(row[6]) + + //device.Version = row[5] + + _, err = dao.DevDevice.Ctx(ctx).Insert(device) + if err != nil { + res.Fail++ + res.DevicesKey = append(res.DevicesKey, device.Key) + } else { + res.Success++ + } + } + return +} + +func (s *sDevDevice) SetDevicesStatus(ctx context.Context, req *product.SetDeviceStatusReq) (res product.SetDeviceStatusRes, err error) { + if req.Status == 1 { + for _, v := range req.Keys { + err = s.Deploy(ctx, v) + if err != nil { + return + } + } + return + } + if req.Status == 0 { + for _, v := range req.Keys { + err = s.Undeploy(ctx, v) + if err != nil { + return + } + } + } + return +} + +// GetDeviceDataList 获取设备属性聚合数据列表 +func (s *sDevDevice) GetDeviceDataList(ctx context.Context, in *model.DeviceDataListInput) (out *model.DeviceDataListOutput, err error) { + device, err := s.Get(ctx, in.DeviceKey) + if err != nil { + return + } + propertys := device.TSL.Properties + if len(propertys) == 0 { + return + } + + fields := []string{"_wstart", "_wend"} + for _, v := range propertys { + key := comm.TsdColumnName(v.Key) + fields = append(fields, fmt.Sprintf("mode(%s) as %s", key, key)) + } + + timeUnit := "m" + switch in.TimeUnit { + case 1: + timeUnit = "s" + case 2: + timeUnit = "m" + case 3: + timeUnit = "h" + case 4: + timeUnit = "d" + } + interval := fmt.Sprintf("interval(%d%s)", in.Interval, timeUnit) + + tsdDb := tsd.DB() + defer tsdDb.Close() + + deviceTable := comm.DeviceTableName(device.Key) + sql := "select count(*) as num from (select count(*) from ? ?)" + rs, err := tsdDb.GetTableDataOne(ctx, sql, deviceTable, interval) + if err != nil { + return + } + out = new(model.DeviceDataListOutput) out.Total = rs["num"].Int() out.CurrentPage = in.PageNum - sql = "select ts, ? from ? where ts >= '?' and ? is not null order by ts desc limit ?, ?" - ls, _ := service.TdEngine().GetAll( + sql = "select ? from ? ? order by _wstart desc limit ?, ?" + out.List, err = tsdDb.GetTableDataAll( ctx, sql, - in.PropertyKey, - p.Key, - gtime.Now().Format("Y-m-d"), - in.PropertyKey, + strings.Join(fields, ","), + deviceTable, + interval, (in.PageNum-1)*in.PageSize, in.PageSize, ) + return +} - var pro []*model.DevicePropertiyOut - for _, v := range ls.List() { +// GetAllForProduct 获取指定产品所有设备 +func (s *sDevDevice) GetAllForProduct(ctx context.Context, productKey string) (list []*entity.DevDevice, err error) { + err = dao.DevDevice.Ctx(ctx).Where(dao.DevDevice.Columns().ProductKey, productKey).Scan(&list) + return +} - pro = append(pro, &model.DevicePropertiyOut{ - Ts: gvar.New(v["ts"]).GTime(), - Value: gvar.New(v[in.PropertyKey]), - }) +// CacheDeviceDetailList 缓存所有设备详情数据 +func (s *sDevDevice) CacheDeviceDetailList(ctx context.Context) (err error) { + productList, err := service.DevProduct().List(context.Background()) + if err != nil { + return } - out.List = pro + for _, p := range productList { + deviceList, err := service.DevDevice().List(context.Background(), p.Key, "") + if err != nil { + g.Log().Error(ctx, err.Error()) + } + + //缓存产品详细信息 + var detailProduct = new(model.DetailProductOutput) + err = gconv.Scan(p, detailProduct) + if err == nil { + err = dcache.SetProductDetailInfo(p.Key, detailProduct) + } else { + g.Log().Error(ctx, err.Error()) + } + for _, d := range deviceList { + if d.Product.Metadata != "" { + err = json.Unmarshal([]byte(d.Product.Metadata), &d.TSL) + d.Product.Metadata = "" + if err != nil { + continue + } + } + //缓存设备详细信息 + err := cache.Instance().Set(context.Background(), consts.DeviceDetailInfoPrefix+d.Key, d, 0) + if err != nil { + g.Log().Error(ctx, err.Error()) + } + } + } return + } diff --git a/internal/logic/product/dev_device_function.go b/internal/logic/product/dev_device_function.go new file mode 100644 index 0000000..f6baa6c --- /dev/null +++ b/internal/logic/product/dev_device_function.go @@ -0,0 +1,47 @@ +package product + +import ( + "context" + "encoding/json" + "errors" + "sagooiot/internal/model" + "sagooiot/internal/service" + dservice "sagooiot/network/core/logic/model/down/service" + "sagooiot/pkg/dcache" + "sagooiot/pkg/iotModel/topicModel" +) + +type sDevDeviceFunction struct{} + +func init() { + service.RegisterDevDeviceFunction(devDeviceFunction()) +} + +func devDeviceFunction() *sDevDeviceFunction { + return &sDevDeviceFunction{} +} + +// Do 执行设备功能 +func (s *sDevDeviceFunction) Do(ctx context.Context, in *model.DeviceFunctionInput) (out *model.DeviceFunctionOutput, err error) { + device, err := dcache.GetDeviceDetailInfo(in.DeviceKey) + if dcache.GetDeviceStatus(ctx, in.DeviceKey) != model.DeviceStatusOn { + err = errors.New("设备不在线") + return + } + + var params []byte + if len(in.Params) > 0 { + if params, err = json.Marshal(in.Params); err != nil { + return + } + } + request := topicModel.TopicDownHandlerData{ + DeviceDetail: device, + PayLoad: params, + } + + out = &model.DeviceFunctionOutput{} + out.Data, err = dservice.ServiceCall(ctx, in.FuncKey, request) + + return +} diff --git a/internal/logic/product/dev_device_function_test.go b/internal/logic/product/dev_device_function_test.go new file mode 100644 index 0000000..bdf4184 --- /dev/null +++ b/internal/logic/product/dev_device_function_test.go @@ -0,0 +1,27 @@ +package product + +import ( + "context" + "sagooiot/internal/model" + "sagooiot/internal/service" + "testing" + + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" +) + +func TestDo(t *testing.T) { + in := &model.DeviceFunctionInput{ + DeviceKey: "aoxiangTest11", + FuncKey: "beginPlay", + Params: map[string]any{ + "deviceNo": "设备编号", + "deviceChannel": "设备通道", + "in_a": 1, + }, + } + out, err := service.DevDeviceFunction().Do(context.TODO(), in) + if err != nil { + t.Fatal(err) + } + t.Log(out) +} diff --git a/internal/logic/product/dev_device_log.go b/internal/logic/product/dev_device_log.go index 168a8cc..b1bd510 100644 --- a/internal/logic/product/dev_device_log.go +++ b/internal/logic/product/dev_device_log.go @@ -2,10 +2,11 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "strings" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/dcache" ) type sDevDeviceLog struct{} @@ -18,40 +19,54 @@ func devDeviceLog() *sDevDeviceLog { return &sDevDeviceLog{} } -// 日志类型 +// LogType 日志类型 func (s *sDevDeviceLog) LogType(ctx context.Context) (list []string) { list = consts.GetTopicTypes() return } -// 日志搜索 +// Search 日志搜索 func (s *sDevDeviceLog) Search(ctx context.Context, in *model.DeviceLogSearchInput) (out *model.DeviceLogSearchOutput, err error) { out = new(model.DeviceLogSearchOutput) - var whereOr []string - for _, v := range in.Types { - whereOr = append(whereOr, "type='"+v+"'") + result, total, currentPage, err := dcache.GetDataByPage(ctx, in.DeviceKey, in.PageNum, in.PageSize, in.Types, in.DateRange) + if err != nil { + return } - where := "" - if len(whereOr) > 0 { - where = " and (" + strings.Join(whereOr, " or ") + ") " - } + out.Total = total + out.CurrentPage = currentPage - if len(in.DateRange) > 0 { - where += " and (ts >= '" + in.DateRange[0] + "' and ts <= '" + in.DateRange[1] + "') " - } - - // TDengine - sql := "select count(*) as num from device_log where device='?'" + where - rs, err := service.TdEngine().GetOne(ctx, sql, in.DeviceKey) - if err != nil { - return + var logs []model.TdLog + if err := gconv.Scan(result, &logs); err != nil { + return nil, err } - out.Total = rs["num"].Int() - out.CurrentPage = in.PageNum + out.List = logs - sql = "select * from device_log where device='?'" + where + " order by ts desc limit ?, ?" - out.List, err = service.TdLogTable().GetAll(ctx, sql, in.DeviceKey, (in.PageNum-1)*in.PageSize, in.PageSize) + //var whereOr []string + //for _, v := range in.Types { + // whereOr = append(whereOr, "type='"+v+"'") + //} + // + //where := "" + //if len(whereOr) > 0 { + // where = " and (" + strings.Join(whereOr, " or ") + ") " + //} + // + //if len(in.DateRange) > 0 { + // where += " and (ts >= '" + in.DateRange[0] + " 00:00:00" + "' and ts <= '" + in.DateRange[1] + " 23:59:59" + "') " + //} + // + //// TDengine + //sql := "select count(*) as num from device_log where device='?'" + where + //rs, err := service.TdEngine().GetOne(ctx, sql, in.DeviceKey) + //if err != nil { + // return + //} + //out.Total = rs["num"].Int() + //out.CurrentPage = in.PageNum + // + //sql = "select * from device_log where device='?'" + where + " order by ts desc limit ?, ?" + //out.List, err = service.TdLogTable().GetAll(ctx, sql, in.DeviceKey, (in.PageNum-1)*in.PageSize, in.PageSize) return } diff --git a/internal/logic/product/dev_device_property.go b/internal/logic/product/dev_device_property.go new file mode 100644 index 0000000..96334b2 --- /dev/null +++ b/internal/logic/product/dev_device_property.go @@ -0,0 +1,61 @@ +package product + +import ( + "context" + "encoding/json" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + dset "sagooiot/network/core/logic/model/down/property/set" + "sagooiot/pkg/dcache" + "sagooiot/pkg/iotModel/topicModel" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/os/gtime" +) + +type sDevDeviceProperty struct{} + +func init() { + service.RegisterDevDeviceProperty(devDeviceProperty()) +} + +func devDeviceProperty() *sDevDeviceProperty { + return &sDevDeviceProperty{} +} + +// Set 设备属性设置 +func (s *sDevDeviceProperty) Set(ctx context.Context, in *model.DevicePropertyInput) (out *model.DevicePropertyOutput, err error) { + device, err := dcache.GetDeviceDetailInfo(in.DeviceKey) + if dcache.GetDeviceStatus(ctx, in.DeviceKey) != model.DeviceStatusOn { + err = gerror.New("设备不在线") + return + } + + var params []byte + if len(in.Params) > 0 { + if params, err = json.Marshal(in.Params); err != nil { + return + } + } + request := topicModel.TopicDownHandlerData{ + DeviceDetail: device, + PayLoad: params, + } + + out = &model.DevicePropertyOutput{} + if out.Data, err = dset.PropertySet(ctx, request); err != nil { + return + } + + // 写日志 + logData := &model.TdLogAddInput{ + Ts: gtime.Now(), + Device: in.DeviceKey, + Type: consts.MsgTypePropertyWrite, + Content: string(params), + } + err = service.TdLogTable().Insert(ctx, logData) + + return +} diff --git a/internal/logic/product/dev_device_property_test.go b/internal/logic/product/dev_device_property_test.go new file mode 100644 index 0000000..0395adb --- /dev/null +++ b/internal/logic/product/dev_device_property_test.go @@ -0,0 +1,25 @@ +package product + +import ( + "context" + "sagooiot/internal/model" + "sagooiot/internal/service" + "testing" + + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" +) + +func TestSet(t *testing.T) { + in := &model.DevicePropertyInput{ + DeviceKey: "aoxiangTest11", + Params: map[string]any{ + "a": 9, + "bb": true, + }, + } + out, err := service.DevDeviceProperty().Set(context.TODO(), in) + if err != nil { + t.Fatal(err) + } + t.Log(out) +} diff --git a/internal/logic/product/dev_device_tag.go b/internal/logic/product/dev_device_tag.go index 6861586..9eb9f97 100644 --- a/internal/logic/product/dev_device_tag.go +++ b/internal/logic/product/dev_device_tag.go @@ -2,10 +2,11 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/os/gtime" @@ -31,15 +32,25 @@ func (s *sDevDeviceTag) Add(ctx context.Context, in *model.AddTagDeviceInput) (e if err != nil { return } - param.CreateBy = uint(loginUserId) + /*param.CreateBy = uint(loginUserId)*/ - _, err = dao.DevDeviceTag.Ctx(ctx).Data(param).Insert() + _, err = dao.DevDeviceTag.Ctx(ctx).Data(do.DevDeviceTag{ + DeptId: service.Context().GetUserDeptId(ctx), + DeviceId: param.DeviceId, + DeviceKey: param.DeviceKey, + Key: param.Key, + Name: param.Name, + Value: param.Value, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() return } func (s *sDevDeviceTag) Edit(ctx context.Context, in *model.EditTagDeviceInput) (err error) { - total, _ := dao.DevDeviceTag.Ctx(ctx).Where(dao.DevDeviceTag.Columns().Id, in.Id).Count() - if total == 0 { + var deviceTag *entity.DevDeviceTag + err = dao.DevDeviceTag.Ctx(ctx).Where(dao.DevDeviceTag.Columns().Id, in.Id).Scan(&deviceTag) + if deviceTag == nil { return gerror.New("标签不存在") } @@ -51,7 +62,7 @@ func (s *sDevDeviceTag) Edit(ctx context.Context, in *model.EditTagDeviceInput) if err != nil { return } - param.UpdateBy = uint(loginUserId) + param.UpdatedBy = uint(loginUserId) param.Id = nil _, err = dao.DevDeviceTag.Ctx(ctx).Data(param).Where(dao.DevDeviceTag.Columns().Id, in.Id).Update() @@ -59,8 +70,9 @@ func (s *sDevDeviceTag) Edit(ctx context.Context, in *model.EditTagDeviceInput) } func (s *sDevDeviceTag) Del(ctx context.Context, id uint) (err error) { - total, _ := dao.DevDeviceTag.Ctx(ctx).Where(dao.DevDeviceTag.Columns().Id, id).Count() - if total == 0 { + var deviceTag *entity.DevDeviceTag + err = dao.DevDeviceTag.Ctx(ctx).Where(dao.DevDeviceTag.Columns().Id, id).Scan(&deviceTag) + if deviceTag == nil { return gerror.New("标签不存在") } @@ -77,3 +89,41 @@ func (s *sDevDeviceTag) Del(ctx context.Context, id uint) (err error) { Update() return } + +func (s *sDevDeviceTag) Update(ctx context.Context, deviceId uint, list []model.AddTagDeviceInput) (err error) { + var tagIds []int + var add []model.AddTagDeviceInput + for _, v := range list { + rs, err := dao.DevDeviceTag.Ctx(ctx). + Fields(dao.DevDeviceTag.Columns().Id). + Where(dao.DevDeviceTag.Columns().DeviceId, deviceId). + Where(dao.DevDeviceTag.Columns().Key, v.Key). + Where(dao.DevDeviceTag.Columns().Name, v.Name). + Where(dao.DevDeviceTag.Columns().Value, v.Value). + Value() + if err != nil { + return err + } + if rs.Int() > 0 { + tagIds = append(tagIds, rs.Int()) + } else { + add = append(add, v) + } + } + if len(tagIds) > 0 { + _, err = dao.DevDeviceTag.Ctx(ctx). + Where(dao.DevDeviceTag.Columns().DeviceId, deviceId). + WhereNotIn(dao.DevDeviceTag.Columns().Id, tagIds). + Unscoped().Delete() + if err != nil { + return + } + } + for _, v := range add { + newV := v + if err = service.DevDeviceTag().Add(ctx, &newV); err != nil { + return + } + } + return +} diff --git a/internal/logic/product/dev_device_test.go b/internal/logic/product/dev_device_test.go index 649dd2c..8673645 100644 --- a/internal/logic/product/dev_device_test.go +++ b/internal/logic/product/dev_device_test.go @@ -2,41 +2,107 @@ package product import ( "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/tdengine" - "github.com/sagoo-cloud/sagooiot/internal/service" + _ "sagooiot/internal/logic/alarm" + _ "sagooiot/internal/logic/common" + _ "sagooiot/internal/logic/configure" + _ "sagooiot/internal/logic/context" + _ "sagooiot/internal/logic/datahub" + _ "sagooiot/internal/logic/dataview" + _ "sagooiot/internal/logic/envirotronics" + _ "sagooiot/internal/logic/gentools" + _ "sagooiot/internal/logic/middleware" + _ "sagooiot/internal/logic/network" + _ "sagooiot/internal/logic/notice" + _ "sagooiot/internal/logic/operate" + _ "sagooiot/internal/logic/rules_engine" + _ "sagooiot/internal/logic/scene" + _ "sagooiot/internal/logic/screen" + _ "sagooiot/internal/logic/system" + _ "sagooiot/internal/logic/tdengine" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + "github.com/gogf/gf/v2/frame/g" ) -func TestTotal(t *testing.T) { - out, err := service.DevDevice().Total(context.TODO()) +func TestRunStatus(t *testing.T) { + out, err := service.DevDevice().RunStatus(context.TODO(), "t20221222") if err != nil { t.Fatal(err) } t.Log(out) } -func TestTotalForMonths(t *testing.T) { - out, err := service.DevDevice().TotalForMonths(context.TODO()) +func TestCheckBind(t *testing.T) { + in := &model.CheckBindInput{ + GatewayKey: "aoxiang_d_gw", + SubKey: "aoxiang_d_sub", + } + yes, err := service.DevDevice().CheckBind(context.TODO(), in) if err != nil { t.Fatal(err) } - t.Log(out) + t.Log(yes) } -func TestAlarmTotalForMonths(t *testing.T) { - out, err := service.DevDevice().AlarmTotalForMonths(context.TODO()) +func TestAuthInfo(t *testing.T) { + in := &model.AuthInfoInput{ + DeviceKey: "aoxiangTest1", + ProductKey: "", + } + out, err := service.DevDevice().AuthInfo(context.TODO(), in) if err != nil { t.Fatal(err) } t.Log(out) } -func TestRunStatus(t *testing.T) { - out, err := service.DevDevice().RunStatus(context.TODO(), 21) +func TestGetData(t *testing.T) { + in := &model.DeviceGetDataInput{ + DeviceKey: "t20221222", + PropertyKey: "va", + DateRange: []string{"2023-05-30 00:00:00", "2023-05-30 00:00:30"}, + } + out, err := service.DevDevice().GetData(context.TODO(), in) if err != nil { t.Fatal(err) } - t.Log(out) + g.Dump(out) +} + +func TestGetPropertyList(t *testing.T) { + in := &model.DeviceGetPropertyListInput{ + DeviceKey: "t20221222", + PropertyKey: "va", + PaginationInput: model.PaginationInput{PageNum: 1, PageSize: 10}, + } + out, err := service.DevDevice().GetPropertyList(context.TODO(), in) + if err != nil { + t.Fatal(err) + } + g.Dump(out) +} + +func TestGetLatestProperty(t *testing.T) { + list, err := service.DevDevice().GetLatestProperty(context.TODO(), "aoxiang925d") + if err != nil { + t.Fatal(err) + } + g.Dump(list) +} + +// TestGetDeviceOnlineTimeOut 测试获取设备在线超时时间 +func TestGetDeviceOnlineTimeOut(t *testing.T) { + timeOut := service.DevDevice().GetDeviceOnlineTimeOut(context.TODO(), "t202210000") + g.Dump(timeOut) +} + +func TestCacheDeviceDetailList(t *testing.T) { + err := service.DevDevice().CacheDeviceDetailList(context.Background()) + if err != nil { + t.Fatal(err) + } + } diff --git a/internal/logic/product/dev_device_tree.go b/internal/logic/product/dev_device_tree.go new file mode 100644 index 0000000..9752216 --- /dev/null +++ b/internal/logic/product/dev_device_tree.go @@ -0,0 +1,246 @@ +package product + +import ( + "context" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/guid" +) + +type sDevDeviceTree struct{} + +func init() { + service.RegisterDevDeviceTree(devDeviceTreeNew()) +} + +func devDeviceTreeNew() *sDevDeviceTree { + return &sDevDeviceTree{} +} + +// List 设备树列表 +func (s *sDevDeviceTree) List(ctx context.Context) (out []*model.DeviceTreeListOutput, err error) { + + var list []*model.DeviceTree + if err = dao.DevDeviceTree.Ctx(ctx).OrderAsc(dao.DevDeviceTree.Columns().Id).Scan(&list); err != nil || len(list) == 0 { + return nil, err + } + + m := dao.DevDeviceTreeInfo.Ctx(ctx) + + var infoList []*entity.DevDeviceTreeInfo + if err = m.Scan(&infoList); err != nil || len(infoList) == 0 { + return nil, err + } + + infoMap := make(map[int]string, len(infoList)) + + var deviceTreeList []*model.DeviceTree + + for _, v := range infoList { + infoMap[v.Id] = v.Name + for _, l := range list { + if v.Id == l.InfoId { + deviceTreeList = append(deviceTreeList, l) + break + } + } + } + for i, v := range deviceTreeList { + deviceTreeList[i].Name = infoMap[v.InfoId] + } + + return tree(deviceTreeList, 0), nil +} + +func tree(all []*model.DeviceTree, pid int) (rs []*model.DeviceTreeListOutput) { + for _, v := range all { + if v.ParentInfoId == pid { + var out *model.DeviceTreeListOutput + if err := gconv.Scan(v, &out); err != nil { + return + } + out.Children = tree(all, v.InfoId) + rs = append(rs, out) + } + } + return +} + +// Change 更换上下级 +func (s *sDevDeviceTree) Change(ctx context.Context, infoId, parentInfoId int) error { + _, err := dao.DevDeviceTree.Ctx(ctx).Where(dao.DevDeviceTree.Columns().InfoId, infoId).Update(g.Map{ + dao.DevDeviceTree.Columns().ParentInfoId: parentInfoId, + }) + return err +} + +// Detail 信息详情 +func (s *sDevDeviceTree) Detail(ctx context.Context, infoId int) (out *model.DetailDeviceTreeInfoOutput, err error) { + if err = dao.DevDeviceTreeInfo.Ctx(ctx).Where(dao.DevDeviceTreeInfo.Columns().Id, infoId).Scan(&out); err != nil || out == nil { + return + } + rs, err := dao.DevDeviceTree.Ctx(ctx).Fields(dao.DevDeviceTree.Columns().ParentInfoId).Where(dao.DevDeviceTree.Columns().InfoId, infoId).Value() + if err != nil { + return + } + out.ParentId = rs.Int() + return +} + +// check 检查设备是否被绑定:true=可绑定 +func (s *sDevDeviceTree) check(ctx context.Context, deviceKey string, infoId int) (b bool, err error) { + if deviceKey == "" { + b = true + return + } + m := dao.DevDeviceTreeInfo.Ctx(ctx).Where(dao.DevDeviceTreeInfo.Columns().DeviceKey, deviceKey) + if infoId > 0 { + m = m.WhereNot(dao.DevDeviceTreeInfo.Columns().Id, infoId) + } + rs, err := m.Count() + if err != nil { + return + } + return rs == 0, nil +} + +// Add 添加设备树基本信息 +func (s *sDevDeviceTree) Add(ctx context.Context, in *model.AddDeviceTreeInfoInput) error { + // 获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + if in.DeviceKey != "" { + _, err := service.DevDevice().Get(ctx, in.DeviceKey) + if err != nil { + return err + } + } + + b, err := s.check(ctx, in.DeviceKey, 0) + if err != nil { + return err + } + if !b { + return gerror.New("该设备已被绑定") + } + + err = dao.DevDeviceTreeInfo.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + result, err := dao.DevDeviceTreeInfo.Ctx(ctx).Data(do.DevDeviceTreeInfo{ + Code: guid.S(), + DeptId: service.Context().GetUserDeptId(ctx), + Name: in.Name, + Address: in.Address, + Lng: in.Lng, + Lat: in.Lat, + Contact: in.Contact, + Phone: in.Phone, + StartDate: in.StartDate, + EndDate: in.EndDate, + Image: in.Image, + DeviceKey: in.DeviceKey, + Duration: in.Duration, + TimeUnit: in.TimeUnit, + Template: in.Template, + Category: in.Category, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() + if err != nil { + return err + } + + infoId, err := service.Sequences().GetSequences(ctx, result, dao.DevDeviceTreeInfo.Table(), dao.DevDeviceTreeInfo.Columns().Id) + if err != nil { + return err + } + + _, err = dao.DevDeviceTree.Ctx(ctx).Data(do.DevDeviceTree{ + InfoId: infoId, + ParentInfoId: in.ParentId, + }).Insert() + + return err + }) + return err +} + +// Edit 修改设备树基本信息 +func (s *sDevDeviceTree) Edit(ctx context.Context, in *model.EditDeviceTreeInfoInput) error { + // 获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + if in.DeviceKey != "" { + _, err := service.DevDevice().Get(ctx, in.DeviceKey) + if err != nil { + return err + } + } + + b, err := s.check(ctx, in.DeviceKey, in.Id) + if err != nil { + return err + } + if !b { + return gerror.New("该设备已被绑定") + } + + var param *do.DevDeviceTreeInfo + if err := gconv.Scan(in, ¶m); err != nil { + return err + } + param.UpdatedBy = uint(loginUserId) + + err = dao.DevDeviceTreeInfo.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + _, err := dao.DevDeviceTreeInfo.Ctx(ctx).Where(dao.DevDeviceTreeInfo.Columns().Id, in.Id).Update(param) + if err != nil { + return err + } + relation := g.Map{ + dao.DevDeviceTree.Columns().ParentInfoId: in.ParentId, + } + _, err = dao.DevDeviceTree.Ctx(ctx).Where(dao.DevDeviceTree.Columns().InfoId, in.Id).Update(relation) + return err + }) + return err +} + +// Del 删除设备树基本信息 +func (s *sDevDeviceTree) Del(ctx context.Context, infoId int) error { + // 获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + n, err := dao.DevDeviceTree.Ctx(ctx).Where(dao.DevDeviceTree.Columns().ParentInfoId, infoId).Count() + if err != nil { + return err + } + if n > 0 { + return gerror.New("请先处理该信息的子集关系") + } + + err = dao.DevDeviceTreeInfo.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + _, err := dao.DevDeviceTreeInfo.Ctx(ctx). + Data(do.DevDeviceTreeInfo{ + DeletedBy: uint(loginUserId), + DeletedAt: gtime.Now(), + }). + Where(dao.DevDeviceTreeInfo.Columns().Id, infoId). + Unscoped(). + Update() + if err != nil { + return err + } + + _, err = dao.DevDeviceTree.Ctx(ctx).Where(dao.DevDeviceTree.Columns().InfoId, infoId).Delete() + return err + }) + return err +} diff --git a/internal/logic/product/dev_device_tree_test.go b/internal/logic/product/dev_device_tree_test.go new file mode 100644 index 0000000..ec0b6f7 --- /dev/null +++ b/internal/logic/product/dev_device_tree_test.go @@ -0,0 +1,17 @@ +package product + +import ( + "context" + "testing" + + "github.com/gogf/gf/v2/frame/g" +) + +func TestDevDeviceTreeNew(t *testing.T) { + treeObj := devDeviceTreeNew() + list, err := treeObj.List(context.TODO()) + if err != nil { + t.Fatal(err) + } + g.Dump(list) +} diff --git a/internal/logic/product/dev_init.go b/internal/logic/product/dev_init.go new file mode 100644 index 0000000..5362393 --- /dev/null +++ b/internal/logic/product/dev_init.go @@ -0,0 +1,135 @@ +package product + +import ( + "context" + "encoding/json" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/tsd/comm" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +type sDevInit struct{} + +func init() { + service.RegisterDevInit(devInitNew()) +} + +func devInitNew() *sDevInit { + return &sDevInit{} +} + +// InitProductForTd 产品表结构初始化 +func (s *sDevInit) InitProductForTd(ctx context.Context) (err error) { + // 资源锁 + lockKey := "tdLock:initProductTable" + lockVal, err := g.Redis().Do(ctx, "SET", lockKey, gtime.Now().Unix(), "NX", "EX", "3600") + if err != nil { + return + } + if lockVal.String() != "OK" { + return + } + defer func() { + _, err = g.Redis().Do(ctx, "DEL", lockKey) + }() + + var list []*entity.DevProduct + c := dao.DevProduct.Columns() + err = dao.DevProduct.Ctx(ctx).Where(c.Status, model.ProductStatusOn).Where(c.MetadataTable, 1).Scan(&list) + if err != nil || len(list) == 0 { + return + } + + // 检测td表结构是否存在,不存在则创建 + for _, p := range list { + stable := comm.ProductTableName(p.Key) + b, _ := service.TSLTable().CheckStable(ctx, stable) + + if b { + continue + } + + var tsl *model.TSL + err = json.Unmarshal([]byte(p.Metadata), &tsl) + if err != nil { + g.Log().Error(ctx, err) + continue + } + if len(tsl.Properties) == 0 { + g.Log().Errorf(ctx, "产品 %s 物模型数据异常", p.Key) + continue + } + + err = service.TSLTable().CreateStable(ctx, tsl) + if err != nil { + g.Log().Error(ctx, err) + continue + } + } + + return nil +} + +// InitDeviceForTd 设备表结构初始化 +func (s *sDevInit) InitDeviceForTd(ctx context.Context) (err error) { + // 资源锁 + lockKey := "tdLock:initDeviceTable" + lockVal, err := g.Redis().Do(ctx, "SET", lockKey, gtime.Now().Unix(), "NX", "EX", "3600") + if err != nil { + return + } + if lockVal.String() != "OK" { + return + } + defer func() { + _, err = g.Redis().Do(ctx, "DEL", lockKey) + }() + + var list []*entity.DevDevice + c := dao.DevDevice.Columns() + err = dao.DevDevice.Ctx(ctx).WhereGT(c.Status, model.DeviceStatusNoEnable).Where(c.MetadataTable, 1).Scan(&list) + if err != nil || len(list) == 0 { + return + } + + // 检测td表结构是否存在,不存在则创建 + for _, d := range list { + + // 检测设备表是否创建TD表的标识 + if d.MetadataTable == 1 { + continue + } + + pd, err := service.DevProduct().Detail(ctx, d.ProductKey) + if err != nil { + g.Log().Error(ctx, err) + continue + } + if pd == nil { + g.Log().Errorf(ctx, "设备 %s 所属产品不存在", d.Key) + continue + } + + table := comm.DeviceTableName(d.Key) + b, _ := service.TSLTable().CheckTable(ctx, table) + + if b { + continue + } + + d := d + go func() { + err = service.TSLTable().CreateTable(ctx, pd.Key, d.Key) + if err != nil { + g.Log().Errorf(ctx, "设备 %s(%s) 建表失败: %s", d.Key, pd.Key, err.Error()) + } + }() + } + + return nil +} diff --git a/internal/logic/product/dev_init_test.go b/internal/logic/product/dev_init_test.go new file mode 100644 index 0000000..f5aa927 --- /dev/null +++ b/internal/logic/product/dev_init_test.go @@ -0,0 +1,20 @@ +package product + +import ( + "context" + "testing" +) + +func TestInitProductForTd(t *testing.T) { + s := devInitNew() + if err := s.InitProductForTd(context.TODO()); err != nil { + t.Fatal(err) + } +} + +func TestInitDeviceForTd(t *testing.T) { + s := devInitNew() + if err := s.InitDeviceForTd(context.TODO()); err != nil { + t.Fatal(err) + } +} diff --git a/internal/logic/product/dev_product.go b/internal/logic/product/dev_product.go index aa55276..07be884 100644 --- a/internal/logic/product/dev_product.go +++ b/internal/logic/product/dev_product.go @@ -3,11 +3,16 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "fmt" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/tsd/comm" + "time" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" @@ -26,8 +31,12 @@ func productNew() *sDevProduct { return &sDevProduct{} } -func (s *sDevProduct) Get(ctx context.Context, key string) (out *model.DetailProductOutput, err error) { - err = dao.DevProduct.Ctx(ctx).WithAll().Where(dao.DevProduct.Columns().Key, key).Scan(&out) +func (s *sDevProduct) Detail(ctx context.Context, key string) (out *model.DetailProductOutput, err error) { + err = dao.DevProduct.Ctx(ctx).Cache(gdb.CacheOption{ + Duration: time.Second * 30, + Name: consts.GetDetailProductOutput + key, + Force: false, + }).WithAll().Where(dao.DevProduct.Columns().Key, key).Scan(&out) if err != nil || out == nil { return } @@ -43,40 +52,11 @@ func (s *sDevProduct) Get(ctx context.Context, key string) (out *model.DetailPro out.CategoryName = out.Category.Name } - // 获取产品的设备数量 - totals, err := service.DevDevice().TotalByProductId(ctx, []uint{out.Id}) - if err != nil { - return - } - out.DeviceTotal = totals[out.Id] - return } -func (s *sDevProduct) Detail(ctx context.Context, id uint) (out *model.DetailProductOutput, err error) { - err = dao.DevProduct.Ctx(ctx).WithAll().Where(dao.DevProduct.Columns().Id, id).Scan(&out) - if err != nil || out == nil { - return - } - - if out.Metadata != "" { - err = json.Unmarshal([]byte(out.Metadata), &out.TSL) - if err != nil { - return - } - } - - if out.Category != nil { - out.CategoryName = out.Category.Name - } - - // 获取产品的设备数量 - totals, err := service.DevDevice().TotalByProductId(ctx, []uint{out.Id}) - if err != nil { - return - } - out.DeviceTotal = totals[out.Id] - +func (s *sDevProduct) GetInfoById(ctx context.Context, id uint) (out *entity.DevProduct, err error) { + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, id).Scan(&out) return } @@ -111,7 +91,7 @@ func (s *sDevProduct) ListForPage(ctx context.Context, in *model.ListForPageInpu m := dao.DevProduct.Ctx(ctx).OrderDesc(c.Id) if in.Status != "" { - m = m.Where(c.Status+" = ", gconv.Int(in.Status)) + m = m.Where(c.Status, gconv.Int(in.Status)) } if in.CategoryId > 0 { m = m.Where(c.CategoryId, in.CategoryId) @@ -137,9 +117,9 @@ func (s *sDevProduct) ListForPage(ctx context.Context, in *model.ListForPageInpu } dLen := len(out.Product) - var productIds = make([]uint, dLen) + var productIds = make([]string, dLen) for i, v := range out.Product { - productIds[i] = v.Id + productIds[i] = v.Key if v.Category != nil { out.Product[i].CategoryName = v.Category.Name @@ -147,19 +127,22 @@ func (s *sDevProduct) ListForPage(ctx context.Context, in *model.ListForPageInpu } // 获取产品的设备数量 - totals, err := service.DevDevice().TotalByProductId(ctx, productIds) + totals, err := service.DevDevice().TotalByProductKey(ctx, productIds) if err != nil { return } for i, v := range out.Product { - out.Product[i].DeviceTotal = totals[v.Id] + out.Product[i].DeviceTotal = totals[v.Key] + out.Product[i].Metadata = "" + } return } func (s *sDevProduct) List(ctx context.Context) (list []*model.ProductOutput, err error) { - err = dao.DevProduct.Ctx(ctx).WithAll(). + m := dao.DevProduct.Ctx(ctx) + err = m.WithAll(). Where(dao.DevProduct.Columns().Status, model.ProductStatusOn). OrderDesc(dao.DevProduct.Columns().Id). Scan(&list) @@ -175,15 +158,7 @@ func (s *sDevProduct) List(ctx context.Context) (list []*model.ProductOutput, er if v.Category != nil { list[i].CategoryName = v.Category.Name } - } - - // 获取产品的设备数量 - totals, err := service.DevDevice().TotalByProductId(ctx, productIds) - if err != nil { - return - } - for i, v := range list { - list[i].DeviceTotal = totals[v.Id] + list[i].Metadata = "" } return @@ -201,37 +176,70 @@ func (s *sDevProduct) Add(ctx context.Context, in *model.AddProductInput) (err e //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - var param *do.DevProduct - err = gconv.Scan(in, ¶m) - if err != nil { - return - } - param.CreateBy = uint(loginUserId) - param.Status = 0 - tsl := &model.TSL{ Key: in.Key, Name: in.Name, } - param.Metadata, err = json.Marshal(tsl) + metadata, err := json.Marshal(tsl) if err != nil { return } - _, err = dao.DevProduct.Ctx(ctx).Data(param).Insert() + _, err = dao.DevProduct.Ctx(ctx).Data(do.DevProduct{ + DeptId: service.Context().GetUserDeptId(ctx), + Key: in.Key, + Name: in.Name, + CategoryId: in.CategoryId, + MessageProtocol: in.MessageProtocol, + TransportProtocol: in.TransportProtocol, + DeviceType: in.DeviceType, + Desc: in.Desc, + Icon: in.Icon, + Metadata: metadata, + Status: 0, + AuthType: in.AuthType, + AuthUser: in.AuthUser, + AuthPasswd: in.AuthPasswd, + AccessToken: in.AccessToken, + CertificateId: in.CertificateId, + ScriptInfo: in.ScriptInfo, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() return } func (s *sDevProduct) Edit(ctx context.Context, in *model.EditProductInput) (err error) { - total, err := dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.Id).Count() + devProduct, err := s.Detail(ctx, in.Key) + if devProduct == nil { + return gerror.New("产品不存在") + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + var param *do.DevProduct + err = gconv.Scan(in, ¶m) if err != nil { return } - if total == 0 { + param.UpdatedBy = uint(loginUserId) + param.Id = nil + + _, err = dao.DevProduct.Ctx(ctx).Data(param).Where(dao.DevProduct.Columns().Key, in.Key).Update() + + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+devProduct.Key) + + return +} + +func (s *sDevProduct) UpdateExtend(ctx context.Context, in *model.ExtendInput) (err error) { + devProduct, err := s.Detail(ctx, in.Key) + if devProduct == nil { return gerror.New("产品不存在") } - //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) @@ -240,33 +248,55 @@ func (s *sDevProduct) Edit(ctx context.Context, in *model.EditProductInput) (err if err != nil { return } - param.UpdateBy = uint(loginUserId) + param.UpdatedBy = uint(loginUserId) param.Id = nil - _, err = dao.DevProduct.Ctx(ctx).Data(param).Where(dao.DevProduct.Columns().Id, in.Id).Update() + _, err = dao.DevProduct.Ctx(ctx).Data(param).Where(dao.DevProduct.Columns().Key, in.Key).Update() + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+devProduct.Key) return } -func (s *sDevProduct) Del(ctx context.Context, ids []uint) (err error) { +func (s *sDevProduct) Del(ctx context.Context, keys []string) (err error) { var p []*entity.DevProduct - err = dao.DevProduct.Ctx(ctx).WhereIn(dao.DevProduct.Columns().Id, ids).Scan(&p) + err = dao.DevProduct.Ctx(ctx).WhereIn(dao.DevProduct.Columns().Key, keys).Scan(&p) if err != nil { return } if len(p) == 0 { return gerror.New("产品不存在") } - if len(p) == 1 && p[0].Status == model.ProductStatusOn { - return gerror.New("产品已发布,不能删除") + + // 状态校验 + for _, v := range p { + if v.Status == model.ProductStatusOn { + return gerror.Newf("产品(%s)已发布,不能删除", v.Key) + } } + //判断产品下是否有未删除的设备 + var devices []*entity.DevDevice + err = dao.DevDevice.Ctx(ctx).WhereIn(dao.DevDevice.Columns().Key, keys).Scan(&devices) + if err != nil { + return + } + for _, device := range devices { + var productName string + for _, v := range p { + if device.ProductKey == v.Key { + productName = v.Name + break + } + } + return gerror.Newf("产品(%s)下有未删除的设备,不能删除", productName) + } //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - for _, id := range ids { - res, _ := s.Detail(ctx, id) + for _, key := range keys { + res, _ := s.Detail(ctx, key) rs, err := dao.DevProduct.Ctx(ctx). Data(do.DevProduct{ @@ -274,7 +304,7 @@ func (s *sDevProduct) Del(ctx context.Context, ids []uint) (err error) { DeletedAt: gtime.Now(), }). Where(dao.DevProduct.Columns().Status, model.ProductStatusOff). - Where(dao.DevProduct.Columns().Id, id). + Where(dao.DevProduct.Columns().Key, key). Unscoped(). Update() if err != nil { @@ -289,79 +319,230 @@ func (s *sDevProduct) Del(ctx context.Context, ids []uint) (err error) { return err } } + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+res.Key) } return } -func (s *sDevProduct) Deploy(ctx context.Context, id uint) (err error) { +// Deploy 产品发布 +func (s *sDevProduct) Deploy(ctx context.Context, productKey string) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, id).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, productKey).Scan(&p) if err != nil { return } if p == nil { return gerror.New("产品不存在") } + if p.Status == model.ProductStatusOn { - return gerror.New("产品已发布") + return + } + if p.Metadata == "" { + return gerror.New("请创建物模型属性") } - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = dao.DevProduct.Ctx(ctx). - Data(g.Map{dao.DevProduct.Columns().Status: model.ProductStatusOn}). - Where(dao.DevProduct.Columns().Id, id). - Update() - if err != nil { - return err - } + var tsl *model.TSL + err = json.Unmarshal([]byte(p.Metadata), &tsl) + if err != nil { + return err + } + if len(tsl.Properties) == 0 { + return gerror.New("请创建物模型属性") + } - // 建立TD表 - if p.Metadata != "" && p.MetadataTable == 0 { - var tsl *model.TSL - err = json.Unmarshal([]byte(p.Metadata), &tsl) - if err != nil { - return err - } + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + // 是否创建TD表结构 + var isCreate bool - err = service.TSLTable().CreateStable(ctx, tsl) + if p.MetadataTable == 0 { + isCreate = true + } + // 检查TD表是否存在 + if p.MetadataTable == 1 { + stable := comm.ProductTableName(p.Key) + b, err := service.TSLTable().CheckStable(ctx, stable) if err != nil { - return err + if err.Error() == "sql: no rows in result set" { + isCreate = true + } else { + return err + } + } + if !b { + isCreate = true } + } - _, err = dao.DevProduct.Ctx(ctx). - Data(g.Map{dao.DevProduct.Columns().MetadataTable: 1}). - Where(dao.DevProduct.Columns().Id, id). - Update() + // 创建TD表结构 + if isCreate { + err = service.TSLTable().CreateStable(ctx, tsl) if err != nil { return err } } - return nil + + // 更新状态 + _, err = dao.DevProduct.Ctx(ctx). + Data(g.Map{ + dao.DevProduct.Columns().Status: model.ProductStatusOn, + dao.DevProduct.Columns().MetadataTable: 1, + }). + Where(dao.DevProduct.Columns().Key, p.Key). + Update() + + return err }) + if err != nil { + return err + } + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+p.Key) return } -func (s *sDevProduct) Undeploy(ctx context.Context, id uint) (err error) { +// Undeploy 产品停用 +func (s *sDevProduct) Undeploy(ctx context.Context, productKey string) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, id).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, productKey).Scan(&p) if err != nil { return } if p == nil { return gerror.New("产品不存在") } + if p.Status == model.ProductStatusOff { - return gerror.New("产品已停用") + return + } + + // 检查是否有启用设备 + in := model.ListDeviceInput{ + ProductKey: p.Key, + } + devList, err := service.DevDevice().List(ctx, gconv.String(in), "") + if err != nil { + return + } + if len(devList) > 0 { + return gerror.New("该产品有启用设备,请先停用设备") } _, err = dao.DevProduct.Ctx(ctx). Data(g.Map{dao.DevProduct.Columns().Status: model.ProductStatusOff}). - Where(dao.DevProduct.Columns().Id, id). + Where(dao.DevProduct.Columns().Key, p.Key). Update() + if err != nil { + return err + } + + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+p.Key) + return +} + +// ListForSub 子设备类型产品 +func (s *sDevProduct) ListForSub(ctx context.Context) (list []*model.ProductOutput, err error) { + m := dao.DevProduct.Ctx(ctx) + err = m.WithAll(). + Where(dao.DevProduct.Columns().DeviceType, model.DeviceTypeSub). + OrderDesc(dao.DevProduct.Columns().Id). + Scan(&list) + return +} + +// UpdateScriptInfo 脚本更新 +func (s *sDevProduct) UpdateScriptInfo(ctx context.Context, in *model.ScriptInfoInput) (err error) { + var devProduct *entity.DevProduct + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.Key).Scan(&devProduct) + if err != nil { + return + } + if devProduct == nil { + return gerror.New("产品不存在") + } + + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + devProduct.ScriptInfo = in.ScriptInfo + devProduct.UpdatedBy = uint(loginUserId) + devProduct.UpdatedAt = gtime.Now() + _, err = dao.DevProduct.Ctx(ctx).Data(devProduct).Where(dao.DevProduct.Columns().Key, in.Key).Update() + //从缓存中删除 + _, err = cache.Instance().Remove(ctx, consts.CacheGfOrmPrefix+consts.GetDetailProductOutput+devProduct.Key) + return +} + +// ConnectIntro 获取设备接入信息 +func (s *sDevProduct) ConnectIntro(ctx context.Context, productKey string) (out *model.DeviceConnectIntroOutput, err error) { + var product *entity.DevProduct + if err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, productKey).Scan(&product); err != nil || product == nil { + return + } + if product.MessageProtocol == "" { + return + } + + // 默认服务 + if product.MessageProtocol == "SagooMqtt" { + mqtt, err := g.Cfg().Get(ctx, "mqtt") + if err != nil { + return nil, err + } + mm := mqtt.MapStrVar() + au := mm["auth"].MapStrStr() + + out = &model.DeviceConnectIntroOutput{ + Name: "默认服务", + Protocol: "SagooMqtt", + Description: "", + Link: fmt.Sprintf("mqtt://%s", mm["addr"].String()), + AuthType: 1, + AuthUser: au["userName"], + AuthPasswd: au["userPassWorld"], + } + return out, nil + } + + // 网络服务 + var networkS *entity.NetworkServer + // TODO status=1 启用 + if err = dao.NetworkServer.Ctx(ctx).WhereLike(dao.NetworkServer.Columns().Protocol, "%"+product.MessageProtocol+"%").Scan(&networkS); err != nil || networkS == nil { + return + } + + out = &model.DeviceConnectIntroOutput{ + Name: networkS.Name, + Protocol: product.MessageProtocol, + Description: networkS.Remark, + Link: fmt.Sprintf("%s://0.0.0.0:%s", networkS.Types, networkS.Addr), + } + + out.AuthType = networkS.AuthType + out.AuthUser = networkS.AuthUser + out.AuthPasswd = networkS.AuthPasswd + out.AccessToken = networkS.AccessToken + out.CertificateId = networkS.CertificateId + + if product.AuthType > 0 { + out.AuthType = product.AuthType + out.AuthUser = product.AuthUser + out.AuthPasswd = product.AuthPasswd + out.AccessToken = product.AccessToken + out.CertificateId = product.CertificateId + } + + if out.CertificateId > 0 { + if cert, err := service.SysCertificate().GetInfoById(ctx, out.CertificateId); err == nil && cert != nil { + out.CertificateName = cert.Name + } + } return } diff --git a/internal/logic/product/dev_tsl_data_type.go b/internal/logic/product/dev_tsl_data_type.go index 40a943d..d60fbb2 100644 --- a/internal/logic/product/dev_tsl_data_type.go +++ b/internal/logic/product/dev_tsl_data_type.go @@ -2,8 +2,8 @@ package product import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/model" + "sagooiot/internal/service" ) type sDevTSLDataType struct{} @@ -47,7 +47,8 @@ func (s *sDevTSLDataType) DataTypeValueList(ctx context.Context) (out *model.Dat // 扩展类型 extensionType := []model.DataTypeValueExtension{ - {Title: "date(时间戳)", Type: "date"}, + {Title: "date(2006-01-02 15:04:05或者2006-01-02 15:04:05.000)", Type: "date"}, + {Title: "timestamp(时间戳/毫秒)", Type: "timestamp"}, {Title: "enum(枚举)", Type: "enum", TSLParamExtension: model.TSLParamExtension{Elements: tEnum}}, {Title: "array(数组)", Type: "array", TSLParamExtension: model.TSLParamExtension{ElementType: tArray}}, {Title: "object(结构体)", Type: "object", TSLParamExtension: model.TSLParamExtension{Properties: tObject}}, diff --git a/internal/logic/product/dev_tsl_event.go b/internal/logic/product/dev_tsl_event.go index 289a388..0f06150 100644 --- a/internal/logic/product/dev_tsl_event.go +++ b/internal/logic/product/dev_tsl_event.go @@ -3,11 +3,12 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" "math" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "strings" "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/errors/gerror" @@ -35,8 +36,7 @@ func (s *sDevTSLEvent) Detail(ctx context.Context, deviceKey string, eventKey st for _, v := range dout.TSL.Events { if v.Key == eventKey { - event = &v - return + return &v, nil } } @@ -46,7 +46,7 @@ func (s *sDevTSLEvent) Detail(ctx context.Context, deviceKey string, eventKey st func (s *sDevTSLEvent) ListEvent(ctx context.Context, in *model.ListTSLEventInput) (out *model.ListTSLEventOutput, err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -86,10 +86,34 @@ func (s *sDevTSLEvent) ListEvent(ctx context.Context, in *model.ListTSLEventInpu return } -func (s *sDevTSLEvent) AddEvent(ctx context.Context, in *model.TSLEventInput) (err error) { +func (s *sDevTSLEvent) AllEvent(ctx context.Context, key string) (list []model.TSLEvent, err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, key).Scan(&p) + if err != nil { + return + } + if p == nil { + return nil, gerror.New("产品不存在") + } + + j, err := gjson.DecodeToJson(p.Metadata) + if err != nil { + return + } + tsl := new(model.TSL) + if err = j.Scan(tsl); err != nil { + return + } + list = tsl.Events + + return +} + +func (s *sDevTSLEvent) AddEvent(ctx context.Context, in *model.TSLEventAddInput) (err error) { + var p *entity.DevProduct + + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -117,16 +141,16 @@ func (s *sDevTSLEvent) AddEvent(ctx context.Context, in *model.TSLEventInput) (e _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return } -func (s *sDevTSLEvent) EditEvent(ctx context.Context, in *model.TSLEventInput) (err error) { +func (s *sDevTSLEvent) EditEvent(ctx context.Context, in *model.TSLEventAddInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -147,7 +171,7 @@ func (s *sDevTSLEvent) EditEvent(ctx context.Context, in *model.TSLEventInput) ( existKey := false existIndex := 0 for i, v := range tsl.Events { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -163,7 +187,7 @@ func (s *sDevTSLEvent) EditEvent(ctx context.Context, in *model.TSLEventInput) ( _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return @@ -172,7 +196,7 @@ func (s *sDevTSLEvent) EditEvent(ctx context.Context, in *model.TSLEventInput) ( func (s *sDevTSLEvent) DelEvent(ctx context.Context, in *model.DelTSLEventInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -193,7 +217,7 @@ func (s *sDevTSLEvent) DelEvent(ctx context.Context, in *model.DelTSLEventInput) existKey := false existIndex := 0 for i, v := range tsl.Events { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -208,7 +232,7 @@ func (s *sDevTSLEvent) DelEvent(ctx context.Context, in *model.DelTSLEventInput) _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return diff --git a/internal/logic/product/dev_tsl_function.go b/internal/logic/product/dev_tsl_function.go index 2360885..4e16887 100644 --- a/internal/logic/product/dev_tsl_function.go +++ b/internal/logic/product/dev_tsl_function.go @@ -3,11 +3,11 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" "math" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" "strings" "github.com/gogf/gf/v2/encoding/gjson" @@ -27,7 +27,7 @@ func devTSLFunctionNew() *sDevTSLFunction { func (s *sDevTSLFunction) ListFunction(ctx context.Context, in *model.ListTSLFunctionInput) (out *model.ListTSLFunctionOutput, err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -109,7 +109,7 @@ func (s *sDevTSLFunction) AllFunction(ctx context.Context, key string, inputsVal func (s *sDevTSLFunction) AddFunction(ctx context.Context, in *model.TSLFunctionAddInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -137,7 +137,7 @@ func (s *sDevTSLFunction) AddFunction(ctx context.Context, in *model.TSLFunction _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return @@ -146,7 +146,7 @@ func (s *sDevTSLFunction) AddFunction(ctx context.Context, in *model.TSLFunction func (s *sDevTSLFunction) EditFunction(ctx context.Context, in *model.TSLFunctionAddInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -167,7 +167,7 @@ func (s *sDevTSLFunction) EditFunction(ctx context.Context, in *model.TSLFunctio existKey := false existIndex := 0 for i, v := range tsl.Functions { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -183,7 +183,7 @@ func (s *sDevTSLFunction) EditFunction(ctx context.Context, in *model.TSLFunctio _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return @@ -192,7 +192,7 @@ func (s *sDevTSLFunction) EditFunction(ctx context.Context, in *model.TSLFunctio func (s *sDevTSLFunction) DelFunction(ctx context.Context, in *model.DelTSLFunctionInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -213,7 +213,7 @@ func (s *sDevTSLFunction) DelFunction(ctx context.Context, in *model.DelTSLFunct existKey := false existIndex := 0 for i, v := range tsl.Functions { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -228,7 +228,7 @@ func (s *sDevTSLFunction) DelFunction(ctx context.Context, in *model.DelTSLFunct _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return diff --git a/internal/logic/product/dev_tsl_import.go b/internal/logic/product/dev_tsl_import.go new file mode 100644 index 0000000..da83e93 --- /dev/null +++ b/internal/logic/product/dev_tsl_import.go @@ -0,0 +1,71 @@ +package product + +import ( + "bytes" + "context" + "encoding/json" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gconv" + "io" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/response" +) + +type sDevTSLImport struct{} + +func init() { + service.RegisterDevTSLImport(devTSLImportNew()) +} + +func devTSLImportNew() *sDevTSLImport { + return &sDevTSLImport{} +} + +// Export 导出物模型 +func (s *sDevTSLImport) Export(ctx context.Context, key string) (err error) { + var product *entity.DevProduct + err = dao.DevProduct.Ctx(ctx).WithAll().Where(dao.DevProduct.Columns().Key, key).Scan(&product) + jsonData := gjson.New(product.Metadata).MustToJson() + reader := bytes.NewReader(jsonData) + var request = g.RequestFromCtx(ctx) + response.ToJsonFIle(request, reader, "TSL-"+key+"-") + + return +} + +// Import 导入物模型 +func (s *sDevTSLImport) Import(ctx context.Context, key string, file *ghttp.UploadFile) (err error) { + jsonData, err := file.Open() + if err != nil { + return err + } + data, err := io.ReadAll(jsonData) + if len(data) < 0 || err != nil { + return err + } + var p *entity.DevProduct + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, key).Scan(&p) + if p == nil || err != nil { + return gerror.New("产品不存在") + } + + var tsl *model.TSL + err = json.Unmarshal(data, &tsl) + if err != nil { + return + } + tsl.Key = p.Key + tsl.Name = p.Name + + _, err = dao.DevProduct.Ctx(ctx). + Data(dao.DevProduct.Columns().Metadata, gconv.String(tsl)). + Where(dao.DevProduct.Columns().Key, key). + Update() + return +} diff --git a/internal/logic/product/dev_tsl_parse.go b/internal/logic/product/dev_tsl_parse.go new file mode 100644 index 0000000..bf4d1ce --- /dev/null +++ b/internal/logic/product/dev_tsl_parse.go @@ -0,0 +1,150 @@ +package product + +import ( + "context" + "encoding/json" + "errors" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/dcache" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "time" +) + +type sDevTSLParse struct { +} + +func init() { + service.RegisterDevTSLParse(New()) +} + +func New() *sDevTSLParse { + return &sDevTSLParse{} +} + +// ParseData 基于物模型解析上报数据 +func (s *sDevTSLParse) ParseData(ctx context.Context, deviceKey string, data []byte) (res iotModel.ReportPropertyData, err error) { + if data == nil || len(data) == 0 { + return nil, errors.New("data is empty") + } + var reportData sagooProtocol.ReportPropertyReq + if err = json.Unmarshal(data, &reportData); err != nil { + g.Log().Errorf(ctx, "parse data error: %s, message:%s, message ignored", err, string(data)) + return + } + if reportData.Params == nil || len(reportData.Params) == 0 { + return nil, errors.New("report data is empty") + } + device, err := dcache.GetDeviceDetailInfo(deviceKey) + if err != nil { + return + } + if device == nil { + g.Log().Errorf(ctx, "device not found, deviceKey:%s", deviceKey) + return nil, errors.New("device not found") + } + switch reportData.Method { + case "thing.event.property.post": + res, err = s.HandleProperties(ctx, device, reportData.Params) + if err != nil { + return + } + // 网关设备上报属性 + case "thing.event.property.pack.post": + for k, v := range reportData.Params { + switch k { + case "properties": + res, err = service.DevTSLParse().HandleProperties(ctx, device, v.(map[string]interface{})) + case "events": + + case "subDevices": + if reportData.Params["subDevices"] == nil { + break + } + subList := v.([]interface{}) + for _, sub := range subList { + subData := sub.(map[string]interface{}) + //获取子设备KEY + subDeviceKey := subData["identity"].(map[string]interface{})["deviceKey"].(string) + subDeviceProperties := subData["properties"].(map[string]interface{}) + if deviceKey == subDeviceKey { + res, err = s.HandleProperties(ctx, device, subDeviceProperties) + } + } + } + + } + } + + return +} + +// HandleProperties 处理属性 +func (s *sDevTSLParse) HandleProperties(ctx context.Context, device *model.DeviceOutput, properties map[string]interface{}) (reportDataInfo iotModel.ReportPropertyData, err error) { + reportDataInfo = make(iotModel.ReportPropertyData) + nowTime := time.Now() + for k, v := range properties { + for _, property := range device.TSL.Properties { + if property.Key == k { + var createTimestamp int64 + var value interface{} + + if mapInfo, ok := v.(map[string]interface{}); ok { + // 处理带时间戳的属性值 + if timeValue, timeOK := mapInfo["time"].(float64); timeOK && mapInfo["value"] != nil { + createTimestamp = int64(timeValue) + value = property.ValueType.ConvertValue(mapInfo["value"]) + } + } else { + // 处理不带时间戳的属性值 + createTimestamp = nowTime.Unix() + value = property.ValueType.ConvertValue(v) + } + + // 构建数据 + reportDataInfo[k] = iotModel.ReportPropertyNode{ + CreateTime: createTimestamp, + Value: value, + } + + break + } + } + } + return +} + +// HandleEvents 处理事件上报 +func (s *sDevTSLParse) HandleEvents(ctx context.Context, device *model.DeviceOutput, events map[string]sagooProtocol.EventNode) (res []iotModel.ReportEventData, err error) { + res = make([]iotModel.ReportEventData, 0, len(events)) + for eventKey, eventData := range events { + // 查找对应的事件定义 + for _, event := range device.TSL.Events { + if event.Key == eventKey { + reportEventData := iotModel.ReportEventData{ + Key: eventKey, + Param: iotModel.ReportEventParam{ + Value: make(map[string]interface{}), + CreateTime: eventData.CreateTime, + }, + } + + // 遍历事件中的每个输出参数 + for _, output := range event.Outputs { + if value, exists := eventData.Value[output.Name]; exists { + reportEventData.Param.Value[output.Name] = output.ValueType.ConvertValue(value) + } + } + + // 上报事件 + if len(reportEventData.Param.Value) > 0 { + res = append(res, reportEventData) + } + break + } + } + } + return +} diff --git a/internal/logic/product/dev_tsl_property.go b/internal/logic/product/dev_tsl_property.go index bfacebc..cba45f1 100644 --- a/internal/logic/product/dev_tsl_property.go +++ b/internal/logic/product/dev_tsl_property.go @@ -3,11 +3,13 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" "math" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "strings" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" @@ -28,7 +30,7 @@ func devTSLPropertyNew() *sDevTSLProperty { func (s *sDevTSLProperty) ListProperty(ctx context.Context, in *model.ListTSLPropertyInput) (out *model.ListTSLPropertyOutput, err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -117,11 +119,16 @@ func (s *sDevTSLProperty) AllProperty(ctx context.Context, key string) (list []m func (s *sDevTSLProperty) AddProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) + if err != nil { + return + } if p == nil { return gerror.New("产品不存在") } - + if p.Status != 0 { + return gerror.New("产品已发布,无法新增!") + } tsl := new(model.TSL) j, err := gjson.DecodeToJson(p.Metadata) if err != nil { @@ -140,10 +147,10 @@ func (s *sDevTSLProperty) AddProperty(ctx context.Context, in *model.TSLProperty tsl.Properties = append(tsl.Properties, in.TSLProperty) metaData, _ := json.Marshal(tsl) - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() if err != nil { return err @@ -169,14 +176,16 @@ func (s *sDevTSLProperty) AddProperty(ctx context.Context, in *model.TSLProperty func (s *sDevTSLProperty) EditProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } if p == nil { return gerror.New("产品不存在") } - + if p.Status != 0 { + return gerror.New("产品已发布,无法修改!") + } j, err := gjson.DecodeToJson(p.Metadata) if err != nil { return @@ -190,7 +199,7 @@ func (s *sDevTSLProperty) EditProperty(ctx context.Context, in *model.TSLPropert existKey := false existIndex := 0 for i, v := range tsl.Properties { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -213,7 +222,7 @@ func (s *sDevTSLProperty) EditProperty(ctx context.Context, in *model.TSLPropert _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() return @@ -222,11 +231,16 @@ func (s *sDevTSLProperty) EditProperty(ctx context.Context, in *model.TSLPropert func (s *sDevTSLProperty) DelProperty(ctx context.Context, in *model.DelTSLPropertyInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) + if err != nil { + return + } if p == nil { return gerror.New("产品不存在") } - + if p.Status != 0 { + return gerror.New("产品已发布,无法删除!") + } j, err := gjson.DecodeToJson(p.Metadata) if err != nil { return @@ -239,8 +253,9 @@ func (s *sDevTSLProperty) DelProperty(ctx context.Context, in *model.DelTSLPrope // 检查属性标识Key是否存在 existKey := false existIndex := 0 + plen := len(tsl.Properties) for i, v := range tsl.Properties { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -253,23 +268,59 @@ func (s *sDevTSLProperty) DelProperty(ctx context.Context, in *model.DelTSLPrope tsl.Properties = append(tsl.Properties[:existIndex], tsl.Properties[existIndex+1:]...) metaData, _ := json.Marshal(tsl) - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { - _, err = dao.DevProduct.Ctx(ctx). - Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). - Update() - if err != nil { - return err - } - + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { + existTable := p.MetadataTable + existStatus := p.Status // 删除TD表字段 - if p.MetadataTable == 1 { - err = service.TSLTable().DelDatabaseField(ctx, p.Key, in.Key) - if err != nil { - return err + if existTable == 1 { + if plen > 1 { + if err = service.TSLTable().DelDatabaseField(ctx, p.Key, in.Key); err != nil { + return err + } + } else { + // 删除超级表 + if err = service.TSLTable().DropStable(ctx, p.Key); err != nil { + return err + } + // 删除子表 + devList, err := service.DevDevice().GetAllForProduct(ctx, p.Key) + if err != nil { + return err + } + for _, v := range devList { + if v.MetadataTable == 0 { + continue + } + if err = service.TSLTable().DropTable(ctx, v.Key); err != nil { + return err + } + _, err = dao.DevDevice.Ctx(ctx). + Data(do.DevDevice{ + MetadataTable: 0, + Status: model.DeviceStatusNoEnable, + }). + Where(dao.DevDevice.Columns().Id, v.Id). + Update() + if err != nil { + return err + } + } + + existTable = 0 + existStatus = model.ProductStatusOff } } - return nil + + // 更新 + _, err = dao.DevProduct.Ctx(ctx). + Data(do.DevProduct{ + MetadataTable: existTable, + Metadata: metaData, + Status: existStatus, + }). + Where(dao.DevProduct.Columns().Key, in.ProductKey). + Update() + return err }) return @@ -278,22 +329,22 @@ func (s *sDevTSLProperty) DelProperty(ctx context.Context, in *model.DelTSLPrope // 检查标识Key是否存在,物模型模块下唯一 func checkExistKey(key string, tsl model.TSL) bool { for _, v := range tsl.Properties { - if v.Key == key { + if strings.EqualFold(v.Key, key) { return true } } for _, v := range tsl.Functions { - if v.Key == key { + if strings.EqualFold(v.Key, key) { return true } } for _, v := range tsl.Events { - if v.Key == key { + if strings.EqualFold(v.Key, key) { return true } } for _, v := range tsl.Tags { - if v.Key == key { + if strings.EqualFold(v.Key, key) { return true } } diff --git a/internal/logic/product/dev_tsl_tag.go b/internal/logic/product/dev_tsl_tag.go index 6d5071f..c808c35 100644 --- a/internal/logic/product/dev_tsl_tag.go +++ b/internal/logic/product/dev_tsl_tag.go @@ -3,11 +3,12 @@ package product import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" "math" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "strings" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/encoding/gjson" @@ -27,7 +28,7 @@ func devTSLTagNew() *sDevTSLTag { func (s *sDevTSLTag) ListTag(ctx context.Context, in *model.ListTSLTagInput) (out *model.ListTSLTagOutput, err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) if err != nil { return } @@ -70,7 +71,10 @@ func (s *sDevTSLTag) ListTag(ctx context.Context, in *model.ListTSLTagInput) (ou func (s *sDevTSLTag) AddTag(ctx context.Context, in *model.TSLTagInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) + if err != nil { + return + } if p == nil { return gerror.New("产品不存在") } @@ -93,10 +97,10 @@ func (s *sDevTSLTag) AddTag(ctx context.Context, in *model.TSLTagInput) (err err tsl.Tags = append(tsl.Tags, in.TSLTag) metaData, _ := json.Marshal(tsl) - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() if err != nil { return err @@ -122,7 +126,10 @@ func (s *sDevTSLTag) AddTag(ctx context.Context, in *model.TSLTagInput) (err err func (s *sDevTSLTag) EditTag(ctx context.Context, in *model.TSLTagInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) + if err != nil { + return + } if p == nil { return gerror.New("产品不存在") } @@ -140,7 +147,7 @@ func (s *sDevTSLTag) EditTag(ctx context.Context, in *model.TSLTagInput) (err er existKey := false existIndex := 0 for i, v := range tsl.Tags { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -154,10 +161,10 @@ func (s *sDevTSLTag) EditTag(ctx context.Context, in *model.TSLTagInput) (err er tsl.Tags = append(newTags, tsl.Tags[existIndex+1:]...) metaData, _ := json.Marshal(tsl) - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() if err != nil { return err @@ -183,7 +190,10 @@ func (s *sDevTSLTag) EditTag(ctx context.Context, in *model.TSLTagInput) (err er func (s *sDevTSLTag) DelTag(ctx context.Context, in *model.DelTSLTagInput) (err error) { var p *entity.DevProduct - err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Id, in.ProductId).Scan(&p) + err = dao.DevProduct.Ctx(ctx).Where(dao.DevProduct.Columns().Key, in.ProductKey).Scan(&p) + if err != nil { + return + } if p == nil { return gerror.New("产品不存在") } @@ -201,7 +211,7 @@ func (s *sDevTSLTag) DelTag(ctx context.Context, in *model.DelTSLTagInput) (err existKey := false existIndex := 0 for i, v := range tsl.Tags { - if v.Key == in.Key { + if strings.EqualFold(v.Key, in.Key) { existKey = true existIndex = i break @@ -214,10 +224,10 @@ func (s *sDevTSLTag) DelTag(ctx context.Context, in *model.DelTSLTagInput) (err tsl.Tags = append(tsl.Tags[:existIndex], tsl.Tags[existIndex+1:]...) metaData, _ := json.Marshal(tsl) - err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error { + err = dao.DevProduct.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error { _, err = dao.DevProduct.Ctx(ctx). Data(dao.DevProduct.Columns().Metadata, metaData). - Where(dao.DevProduct.Columns().Id, in.ProductId). + Where(dao.DevProduct.Columns().Key, in.ProductKey). Update() if err != nil { return err diff --git a/internal/logic/system/captcha.go b/internal/logic/system/captcha.go index 4e1f8e7..6f495e6 100644 --- a/internal/logic/system/captcha.go +++ b/internal/logic/system/captcha.go @@ -3,8 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/text/gstr" - "github.com/sagoo-cloud/sagooiot/internal/service" "image/color" + "sagooiot/internal/service" + "time" "github.com/mojocn/base64Captcha" ) @@ -12,11 +13,11 @@ import ( type sCaptcha struct{} var ( - captchaStore = base64Captcha.DefaultMemStore + captchaStore = base64Captcha.NewMemoryStore(100, 60*time.Second) captchaDriver = &base64Captcha.DriverString{ Height: 80, Width: 240, - NoiseCount: 50, + NoiseCount: 0, //文本噪声计数 ShowLineOptions: 20, Length: 4, Source: "abcdefghjkmnpqrstuvwxyz23456789", @@ -38,7 +39,7 @@ func New() *sCaptcha { func (s *sCaptcha) GetVerifyImgString(ctx context.Context) (idKeyC string, base64stringC string, err error) { driver := captchaDriver.ConvertFonts() c := base64Captcha.NewCaptcha(driver, captchaStore) - idKeyC, base64stringC, err = c.Generate() + idKeyC, base64stringC, _, err = c.Generate() return } diff --git a/internal/logic/system/login.go b/internal/logic/system/login.go index edb953a..20a1326 100644 --- a/internal/logic/system/login.go +++ b/internal/logic/system/login.go @@ -2,19 +2,22 @@ package system import ( "context" + "fmt" "github.com/gogf/gf/v2/crypto/gmd5" "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/guid" - "github.com/mssola/user_agent" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/utils" + "github.com/mssola/useragent" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/utility/utils" + "strconv" "strings" ) @@ -30,12 +33,53 @@ func init() { } // Login 登录 -func (s *sLogin) Login(ctx context.Context, verifyKey string, captcha string, userName string, password string) (loginUserOut *model.LoginUserOut, token string, err error) { +func (s *sLogin) Login(ctx context.Context, verifyKey string, captcha string, userName string, password string) (loginUserOut *model.LoginUserOut, token string, isChangePassword int, err error) { + //判断验证码是否正确 if !service.Captcha().VerifyString(verifyKey, captcha) { err = gerror.New("验证码输入错误") return } + + //判断是否启用了安全控制和启用了RSA + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysIsRsaEnabled} + configDatas, err := service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + isSecurityControlEnabled := "0" //是否启动安全控制 + isRsaEnbled := "0" //是否启用RSA + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysIsSecurityControlEnabled) { + isSecurityControlEnabled = configData.ConfigValue + } + if strings.EqualFold(configData.ConfigKey, consts.SysIsRsaEnabled) { + isRsaEnbled = configData.ConfigValue + } + } + + if strings.EqualFold(isSecurityControlEnabled, "1") { + //验证密码错误次数 + err = s.CheckPwdErrorNum(ctx, userName) + if err != nil { + return + } + + //判断用户是否需要更改密码 + isChangePassword = s.IsChangePwd(ctx, userName) + if isChangePassword == 1 { + return + } + + if strings.EqualFold(isRsaEnbled, "1") { + //对账号进行解密 + password, err = utils.Decrypt(consts.RsaPrivateKeyFile, password, consts.RsaOAEP) + if err != nil { + return + } + } + } + //获取IP地址 ip := utils.GetClientIp(ctx) //获取user-agent @@ -50,14 +94,140 @@ func (s *sLogin) Login(ctx context.Context, verifyKey string, captcha string, us Ip: ip, UserAgent: userAgent, Msg: err.Error(), - Module: "系统后台", + Module: "账号密码登录", }) return } + + loginUserOut, token, err = s.GenUserToken(ctx, isSecurityControlEnabled, ip, userAgent, userInfo, "账号密码登录") + if err != nil { + return + } + + return +} + +// CheckPwdErrorNum 验证密码错误次数 +func (s *sLogin) CheckPwdErrorNum(ctx context.Context, userName string) (err error) { + tmpData, err := cache.Instance().Get(ctx, consts.CacheSysErrorPrefix+"_"+gconv.String(userName)) + if tmpData.Val() != nil { + //获取密码错误次数和限制登录时间 + configKeys := []string{consts.SysPasswordErrorNum, consts.SysAgainLoginDate} + var configDatas []*entity.SysConfig + configDatas, err = service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + errorNum := 3 //密码错误次数 + againLoginDate := 1 //限制登录时间 + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysPasswordErrorNum) { + errorNum, err = strconv.Atoi(configData.ConfigValue) + if err != nil { + err = gerror.New("密码错误次数配置错误") + return + } + } + if strings.EqualFold(configData.ConfigKey, consts.SysAgainLoginDate) { + againLoginDate, err = strconv.Atoi(configData.ConfigValue) + if err != nil { + err = gerror.New("允许再次登录时间配置错误") + return + } + } + } + + tempValue, _ := strconv.Atoi(tmpData.Val().(string)) + if tempValue >= errorNum { + err = gerror.Newf("密码错误次数过多, 请%d分钟后再重试", againLoginDate) + return + } + } + return +} +func (s *sLogin) IsChangePwd(ctx context.Context, userName string) (isChangePwd int) { + + changePasswordForFirstLogin := "0" //是否开启首次登录更改密码 + passwordChangePeriod := "90" //是否开启是否密码定期更换 + + //判断是否启用了安全控制和启用了RSA + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysChangePasswordForFirstLogin, consts.SysPasswordChangePeriodSwitch} + configDatas, err := service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysChangePasswordForFirstLogin) { + changePasswordForFirstLogin = configData.ConfigValue + } + if strings.EqualFold(configData.ConfigKey, consts.SysPasswordChangePeriodSwitch) { + passwordChangePeriod = configData.ConfigValue + } + } + + var userInfo *entity.SysUser + userInfo, err = service.SysUser().GetUserByUsername(ctx, userName) + if userInfo == nil { + isChangePwd = 0 + return + } + + //获取用户修改密码的历史记录 + var history []*entity.SysUserPasswordHistory + err = dao.SysUserPasswordHistory.Ctx(ctx).Where(dao.SysUserPasswordHistory.Columns().UserId, userInfo.Id).OrderDesc(dao.SysUserPasswordHistory.Columns().CreatedAt).Scan(&history) + + if strings.EqualFold(changePasswordForFirstLogin, "1") { + if history == nil { + isChangePwd = 1 + return + } + } + + if strings.EqualFold(passwordChangePeriod, "1") { + //获取密码更换周期录系统参数 + var configDataByPwdChangePeriod *entity.SysConfig + configDataByPwdChangePeriod, err = service.ConfigData().GetConfigByKey(ctx, consts.SysPasswordChangePeriod) + if err != nil { + return + } + if configDataByPwdChangePeriod != nil { + //获取用户修改密码的历史记录 + // 将字符串转换为整数 + var days int + days, err = strconv.Atoi(configDataByPwdChangePeriod.ConfigValue) + if err != nil { + fmt.Println("无法将字符串转换为整数:", err) + return + } + changeTime := gtime.Now() + if history != nil || len(history) > 0 { + changeTime = history[0].ChangeTime + } + if changeTime.AddDate(0, 0, days).Before(gtime.Now()) { + isChangePwd = 1 + return + } + } + } + + return +} + +// GenUserToken 生成用户TOKEN +func (s *sLogin) GenUserToken(ctx context.Context, isSecurityControlEnabled string, ip string, userAgent string, userInfo *entity.SysUser, logMoudel string) (loginUserOut *model.LoginUserOut, token string, err error) { + var configData *entity.SysConfig + if strings.EqualFold(isSecurityControlEnabled, "1") { + //获取是否单一登录系统参数 + configData, err = service.ConfigData().GetConfigByKey(ctx, consts.SysIsSingleLogin) + if err != nil { + return + } + } + //生成token - key := gconv.String(userInfo.Id) + "-" + gmd5.MustEncryptString(userInfo.UserName) + gmd5.MustEncryptString(userInfo.UserPassword) - if g.Cfg().MustGet(ctx, "gfToken.multiLogin").Bool() { - key = gconv.String(userInfo.Id) + "-" + gmd5.MustEncryptString(userInfo.UserName) + "-" + gmd5.MustEncryptString(userInfo.UserPassword+ip+userAgent) + key := "Login:" + gconv.String(userInfo.Id) + "-" + gmd5.MustEncryptString(userInfo.UserName) + if configData != nil && strings.EqualFold(configData.ConfigValue, "1") { + key = "Login:" + gconv.String(userInfo.Id) + "-" + gmd5.MustEncryptString(userInfo.UserName) + "-" + gmd5.MustEncryptString(ip+userAgent) } userInfo.UserPassword = "" token, err = service.SysToken().GenerateToken(ctx, key, userInfo) @@ -76,19 +246,20 @@ func (s *sLogin) Login(ctx context.Context, verifyKey string, captcha string, us // 保存登录成功的日志信息 service.SysLoginLog().Invoke(ctx, &model.LoginLogParams{ Status: 1, - Username: userName, + Username: userInfo.UserName, Ip: ip, UserAgent: userAgent, Msg: "登录成功", - Module: "系统后台", + Module: logMoudel, }) //查看是否在线用户信息是否都存在不存在则删除 userOnlines, _ := service.SysUserOnline().GetAll(ctx) if len(userOnlines) > 0 { - var onlineIds []uint + var onlineIds []int for _, online := range userOnlines { - if !common.Cache().Get(ctx, online.Key).Bool() { + onliceCache, _ := cache.Instance().Get(ctx, online.Key) + if !onliceCache.Bool() { onlineIds = append(onlineIds, online.Id) } } @@ -98,12 +269,12 @@ func (s *sLogin) Login(ctx context.Context, verifyKey string, captcha string, us } //保存在线用户信息 - ua := user_agent.New(userAgent) + ua := useragent.New(userAgent) os := ua.OS() explorer, _ := ua.Browser() service.SysUserOnline().Invoke(ctx, &entity.SysUserOnline{ Uuid: guid.S(), - UserName: userName, + UserName: userInfo.UserName, Key: key, Token: token, Ip: ip, @@ -130,14 +301,12 @@ func (s *sLogin) LoginOut(ctx context.Context) (err error) { err = gerror.New("未登录,无法退出!") return } - cache := common.Cache() //增加删除缓存信息 - cache.Remove(ctx, userOnline.Key) loginUserId := service.Context().GetUserId(ctx) - - cache.Remove(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId)) - cache.Remove(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId)) + _, err = cache.Instance().Remove(ctx, userOnline.Key) + _, err = cache.Instance().Remove(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId)) + _, err = cache.Instance().Remove(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId)) err = service.SysUserOnline().DelByToken(ctx, token[1]) return diff --git a/internal/logic/system/sys_api.go b/internal/logic/system/sys_api.go index 3cc8736..e80f4aa 100644 --- a/internal/logic/system/sys_api.go +++ b/internal/logic/system/sys_api.go @@ -2,16 +2,21 @@ package system import ( "context" + "database/sql" "encoding/json" + "fmt" "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/net/gclient" "github.com/gogf/gf/v2/os/gtime" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "strings" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -31,19 +36,19 @@ func init() { // GetInfoByIds 根据接口APIID数组获取接口信息 func (s *sSysApi) GetInfoByIds(ctx context.Context, ids []int) (data []*entity.SysApi, err error) { - cache := common.Cache() //获取缓存信息 var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysApi) - if err != nil { - return - } + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysApi) + var tmpSysApiInfo []*entity.SysApi var apiInfo []*entity.SysApi //根据菜单ID数组获取菜单列表信息 if tmpData.Val() != nil { - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysApiInfo) + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysApiInfo); err != nil { + return + } + for _, id := range ids { for _, menuTmp := range tmpSysApiInfo { if id == int(menuTmp.Id) { @@ -94,24 +99,29 @@ func (s *sSysApi) GetInfoById(ctx context.Context, id int) (entity *entity.SysAp } // GetApiAll 获取所有接口 -func (s *sSysApi) GetApiAll(ctx context.Context) (data []*entity.SysApi, err error) { - cache := common.Cache() - err = dao.SysApi.Ctx(ctx).Where(g.Map{ +func (s *sSysApi) GetApiAll(ctx context.Context, method string) (data []*entity.SysApi, err error) { + m := dao.SysApi.Ctx(ctx) + if method != "" { + m = m.Where(dao.SysApi.Columns().Method, method) + } + err = m.Where(g.Map{ dao.SysApi.Columns().IsDeleted: 0, dao.SysApi.Columns().Status: 1, dao.SysApi.Columns().Types: 2, }).Scan(&data) - if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysApi, data, 0) - } else { - cache.Remove(ctx, consts.CacheSysApi) + if method == "" { + if data != nil && len(data) > 0 { + err = cache.Instance().Set(ctx, consts.CacheSysApi, data, 0) + } else { + _, err = cache.Instance().Remove(ctx, consts.CacheSysApi) + } } return } // GetApiTree 获取Api数结构数据 func (s *sSysApi) GetApiTree(ctx context.Context, name string, address string, status int, types int) (out []*model.SysApiTreeOut, err error) { - var e []*entity.SysApi + var es []*model.SysApiTreeOut m := dao.SysApi.Ctx(ctx) if name != "" { m = m.WhereLike(dao.SysApi.Columns().Name, "%"+name+"%") @@ -127,10 +137,18 @@ func (s *sSysApi) GetApiTree(ctx context.Context, name string, address string, s } m = m.Where(dao.SysApi.Columns().IsDeleted, 0) - err = m.OrderAsc(dao.SysApi.Columns().Sort).Scan(&e) + err = m.OrderAsc(dao.SysApi.Columns().Sort).Scan(&es) + for _, e := range es { + menuApiInfo, _ := service.SysMenuApi().GetInfoByApiId(ctx, int(e.Id)) + var menuIds []int + for _, menuApi := range menuApiInfo { + menuIds = append(menuIds, menuApi.MenuId) + } + e.MenuIds = append(e.MenuIds, menuIds...) + } - if len(e) > 0 { - out, err = GetApiTree(e) + if len(es) > 0 { + out, err = GetApiTree(es) if err != nil { return } @@ -170,18 +188,18 @@ func (s *sSysApi) Add(ctx context.Context, input *model.AddApiInput) (err error) return gerror.New("上级节点不是分类,无法新增") } } - err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { var apiInfo *entity.SysApi //根据名称查看是否存在 - apiInfo = CheckApiName(ctx, input.Name, 0) + apiInfo = CheckApiName(ctx, input.Name, 0, input.ParentId) if apiInfo != nil { - return gerror.New("Api名字已存在,无法添加") + return gerror.New("同一个分类下名称不能重复") } if input.Types == 2 { //根据名称查看是否存在 - apiInfo = CheckApiAddress(ctx, input.Address, 0) + apiInfo = CheckApiAddress(ctx, input.Address, 0, input.ApiTypes) if apiInfo != nil { - return gerror.New("Api地址,无法添加") + return gerror.New("同一个服务下Api地址,无法添加") } } //获取当前登录用户ID @@ -191,18 +209,40 @@ func (s *sSysApi) Add(ctx context.Context, input *model.AddApiInput) (err error) return } apiInfo.IsDeleted = 0 - apiInfo.CreateBy = uint(loginUserId) - apiInfoId, err := dao.SysApi.Ctx(ctx).Data(apiInfo).InsertAndGetId() + apiInfo.CreatedBy = uint(loginUserId) + result, err := dao.SysApi.Ctx(ctx).Data(do.SysApi{ + ParentId: apiInfo.ParentId, + Name: apiInfo.Name, + Types: apiInfo.Types, + ApiTypes: apiInfo.ApiTypes, + Method: apiInfo.Method, + Address: apiInfo.Address, + Remark: apiInfo.Remark, + Status: apiInfo.Status, + Sort: apiInfo.Sort, + IsDeleted: apiInfo.IsDeleted, + CreatedBy: apiInfo.CreatedBy, + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return err } - //绑定菜单 - err = AddMenuApi(ctx, int(apiInfoId), input.MenuIds, loginUserId) - if err != nil { - return + + if input.Types == 2 { + var lastInsertId int64 + //获取主键ID + lastInsertId, err = service.Sequences().GetSequences(ctx, result, dao.SysApi.Table(), dao.SysApi.Columns().Id) + if err != nil { + return + } + //绑定菜单 + err = s.AddMenuApi(ctx, "api", []int{int(lastInsertId)}, input.MenuIds) + if err != nil { + return + } } //获取所有接口并添加缓存 - _, err = s.GetApiAll(ctx) + _, err = s.GetApiAll(ctx, "") return }) return @@ -228,74 +268,108 @@ func (s *sSysApi) Detail(ctx context.Context, id int) (out *model.SysApiOut, err return } -func AddMenuApi(ctx context.Context, id int, menuIds []int, loginUserId int) (err error) { - cache := common.Cache() - //添加菜单 - var sysMenuApis []*entity.SysMenuApi - for _, menuId := range menuIds { - var menuInfo *entity.SysMenu - err = dao.SysMenu.Ctx(ctx).Where(dao.SysMenu.Columns().Id, menuId).Scan(&menuInfo) - if menuInfo == nil { - err = gerror.New("菜单ID错误") - return - } - if menuInfo != nil && menuInfo.IsDeleted == 1 { - err = gerror.New(menuInfo.Name + "已删除,无法绑定") - return - } - if menuInfo != nil && menuInfo.Status == 0 { - err = gerror.New(menuInfo.Name + "已禁用,无法绑定") - return - } +func (s *sSysApi) AddMenuApi(ctx context.Context, addPageSource string, apiIds []int, menuIds []int) (err error) { + loginUserId := service.Context().GetUserId(ctx) + if addPageSource != "" && strings.EqualFold(addPageSource, "api") { //解除旧绑定关系 _, err = dao.SysMenuApi.Ctx(ctx).Data(g.Map{ dao.SysMenuApi.Columns().IsDeleted: 1, dao.SysMenuApi.Columns().DeletedBy: loginUserId, - }).Where(dao.SysMenuApi.Columns().ApiId, id).Update() - _, err = dao.SysMenuApi.Ctx(ctx).Where(dao.SysMenuApi.Columns().ApiId, id).Delete() - - var sysMenuApi = new(entity.SysMenuApi) - sysMenuApi.MenuId = menuId - sysMenuApi.ApiId = id - sysMenuApi.IsDeleted = 0 - sysMenuApi.CreatedBy = uint(loginUserId) - sysMenuApis = append(sysMenuApis, sysMenuApi) - } - if sysMenuApis != nil { - //添加 - _, addErr := dao.SysMenuApi.Ctx(ctx).Data(sysMenuApis).Insert() - if addErr != nil { - err = gerror.New("添加失败") - return - } - //查询菜单ID绑定的所有接口ID - var menuApiInfos []*entity.SysMenuApi - dao.SysMenuApi.Ctx(ctx).Where(g.Map{ - dao.SysMenuApi.Columns().IsDeleted: 0, - }).WhereIn(dao.SysMenuApi.Columns().MenuId, menuIds).Scan(&menuApiInfos) - //添加缓存 + dao.SysMenuApi.Columns().DeletedAt: gtime.Now(), + }).WhereIn(dao.SysMenuApi.Columns().ApiId, apiIds).Update() + } + if addPageSource != "" && strings.EqualFold(addPageSource, "menu") { + //解除旧绑定关系 + _, err = dao.SysMenuApi.Ctx(ctx).Data(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 1, + dao.SysMenuApi.Columns().DeletedBy: loginUserId, + dao.SysMenuApi.Columns().DeletedAt: gtime.Now(), + }).WhereIn(dao.SysMenuApi.Columns().MenuId, menuIds).Update() + } + + if menuIds != nil && len(menuIds) > 0 && apiIds != nil && len(apiIds) > 0 { + //添加菜单 + var sysMenuApis []*entity.SysMenuApi for _, menuId := range menuIds { - var menuApi []*entity.SysMenuApi - for _, menuApiInfo := range menuApiInfos { - if menuId == menuApiInfo.MenuId { - menuApi = append(menuApi, menuApiInfo) - } + var menuInfo *entity.SysMenu + err = dao.SysMenu.Ctx(ctx).Where(dao.SysMenu.Columns().Id, menuId).Scan(&menuInfo) + if menuInfo == nil { + err = gerror.New("菜单ID错误") + return + } + if menuInfo != nil && menuInfo.IsDeleted == 1 { + err = gerror.New(menuInfo.Name + "已删除,无法绑定") + return } - if menuApi != nil && len(menuApi) > 0 { - cache.Set(ctx, consts.CacheSysMenuApi+"_"+gconv.String(menuId), menuApi, 0) + if menuInfo != nil && menuInfo.Status == 0 { + err = gerror.New(menuInfo.Name + "已禁用,无法绑定") + return } + for _, id := range apiIds { + apiInfo, _ := s.Detail(ctx, id) + if apiInfo == nil { + return gerror.New("API接口不存在") + } + if apiInfo.Types != 2 { + return gerror.New("参数错误") + } + var sysMenuApi = new(entity.SysMenuApi) + sysMenuApi.MenuId = menuId + sysMenuApi.ApiId = id + sysMenuApi.IsDeleted = 0 + sysMenuApi.CreatedBy = uint(loginUserId) + sysMenuApis = append(sysMenuApis, sysMenuApi) + } } - //获取所有信息 - var menuApiInfoAll []*entity.SysMenuApi - dao.SysMenuApi.Ctx(ctx).Where(g.Map{ - dao.SysMenuApi.Columns().IsDeleted: 0, - }).WhereIn(dao.SysMenuApi.Columns().MenuId, menuIds).Scan(&menuApiInfoAll) - if menuApiInfoAll != nil && len(menuApiInfoAll) > 0 { - cache.Set(ctx, consts.CacheSysMenuApi, menuApiInfoAll, 0) + if sysMenuApis != nil { + //添加 + var sysMenuApisInput []*model.SysMenuApiInput + if err = gconv.Scan(sysMenuApis, &sysMenuApisInput); err != nil { + return + } + + _, addErr := dao.SysMenuApi.Ctx(ctx).Data(sysMenuApisInput).Insert() + if addErr != nil { + err = gerror.New("添加失败") + return + } + //查询菜单ID绑定的所有接口ID + var menuApiInfos []*entity.SysMenuApi + err = dao.SysMenuApi.Ctx(ctx).Where(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 0, + }).WhereIn(dao.SysMenuApi.Columns().MenuId, menuIds).Scan(&menuApiInfos) + //添加缓存 + for _, menuId := range menuIds { + var menuApi []*entity.SysMenuApi + for _, menuApiInfo := range menuApiInfos { + if menuId == menuApiInfo.MenuId { + menuApi = append(menuApi, menuApiInfo) + } + } + if menuApi != nil && len(menuApi) > 0 { + err := cache.Instance().Set(ctx, consts.CacheSysMenuApi+"_"+gconv.String(menuId), menuApi, 0) + if err != nil { + return err + } + } + + } + //获取所有信息 + var menuApiInfoAll []*entity.SysMenuApi + err = dao.SysMenuApi.Ctx(ctx).Where(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 0, + }).WhereIn(dao.SysMenuApi.Columns().MenuId, menuIds).Scan(&menuApiInfoAll) + if menuApiInfoAll != nil && len(menuApiInfoAll) > 0 { + err := cache.Instance().Set(ctx, consts.CacheSysMenuApi, menuApiInfoAll, 0) + if err != nil { + return err + } + } } } + return } @@ -331,19 +405,19 @@ func (s *sSysApi) Edit(ctx context.Context, input *model.EditApiInput) (err erro return gerror.New("上级节点不是分类,无法新增") } } - err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { var apiInfo, apiInfo2 *entity.SysApi //根据ID查看Api列表是否存在 apiInfo = CheckApiId(ctx, input.Id, apiInfo) if apiInfo == nil { return gerror.New("Api列表不存在") } - apiInfo2 = CheckApiName(ctx, input.Name, input.Id) + apiInfo2 = CheckApiName(ctx, input.Name, input.Id, input.ParentId) if apiInfo2 != nil { - return gerror.New("相同Api名称已存在,无法修改") + return gerror.New("同一个分类下名称不能重复") } if input.Types == 2 { - apiInfo2 = CheckApiAddress(ctx, input.Address, input.Id) + apiInfo2 = CheckApiAddress(ctx, input.Address, input.Id, input.ApiTypes) if apiInfo2 != nil { return gerror.New("Api地址已存在,无法修改") } @@ -360,10 +434,10 @@ func (s *sSysApi) Edit(ctx context.Context, input *model.EditApiInput) (err erro return gerror.New("修改失败") } //绑定菜单 - err = AddMenuApi(ctx, input.Id, input.MenuIds, loginUserId) + err = s.AddMenuApi(ctx, "api", []int{input.Id}, input.MenuIds) //获取所有接口并添加缓存 - _, err = s.GetApiAll(ctx) + _, err = s.GetApiAll(ctx, "") return }) @@ -397,7 +471,7 @@ func (s *sSysApi) Del(ctx context.Context, Id int) (err error) { return } //开启事务管理 - err = dao.SysApi.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysApi.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { //更新Api列表信息 _, err = dao.SysApi.Ctx(ctx). Data(g.Map{ @@ -408,13 +482,13 @@ func (s *sSysApi) Del(ctx context.Context, Id int) (err error) { Update() //删除于菜单关系绑定 _, err = dao.SysMenuApi.Ctx(ctx).Data(g.Map{ - dao.SysMenuApi.Columns().IsDeleted: 0, + dao.SysMenuApi.Columns().IsDeleted: 1, dao.SysMenuApi.Columns().DeletedBy: loginUserId, dao.SysMenuApi.Columns().DeletedAt: time, }).Where(dao.SysMenuApi.Columns().ApiId, Id).Update() //获取所有接口并添加缓存 - _, err = s.GetApiAll(ctx) + _, err = s.GetApiAll(ctx, "") return }) @@ -445,7 +519,7 @@ func (s *sSysApi) EditStatus(ctx context.Context, id int, status int) (err error }).Update() //获取所有接口并添加缓存 - _, err = s.GetApiAll(ctx) + _, err = s.GetApiAll(ctx, "") return } @@ -460,7 +534,7 @@ func (s *sSysApi) GetInfoByAddress(ctx context.Context, address string) (entity return } -// 检查指定ID的数据是否存在 +// CheckApiId 检查指定ID的数据是否存在 func CheckApiId(ctx context.Context, Id int, apiColumn *entity.SysApi) *entity.SysApi { _ = dao.SysApi.Ctx(ctx).Where(g.Map{ dao.SysApi.Columns().Id: Id, @@ -469,13 +543,14 @@ func CheckApiId(ctx context.Context, Id int, apiColumn *entity.SysApi) *entity.S return apiColumn } -// 检查相同Api名称的数据是否存在 -func CheckApiName(ctx context.Context, name string, tag int) *entity.SysApi { +// CheckApiName 检查相同Api名称的数据是否存在 +func CheckApiName(ctx context.Context, name string, tag int, parentId int) *entity.SysApi { var apiInfo *entity.SysApi m := dao.SysApi.Ctx(ctx) if tag > 0 { m = m.WhereNot(dao.SysApi.Columns().Id, tag) } + m = m.Where(dao.SysApi.Columns().ParentId, parentId) _ = m.Where(g.Map{ dao.SysApi.Columns().Name: name, dao.SysApi.Columns().IsDeleted: 0, @@ -483,16 +558,222 @@ func CheckApiName(ctx context.Context, name string, tag int) *entity.SysApi { return apiInfo } -// 检查相同Api地址的数据是否存在 -func CheckApiAddress(ctx context.Context, address string, tag int) *entity.SysApi { +// CheckApiAddress 检查相同Api地址的数据是否存在 +func CheckApiAddress(ctx context.Context, address string, tag int, apiTypes string) *entity.SysApi { var apiInfo *entity.SysApi m := dao.SysApi.Ctx(ctx) if tag > 0 { m = m.WhereNot(dao.SysApi.Columns().Id, tag) } + m = m.Where(dao.SysApi.Columns().ApiTypes, apiTypes) _ = m.Where(g.Map{ dao.SysApi.Columns().Address: address, dao.SysApi.Columns().IsDeleted: 0, }).Scan(&apiInfo) return apiInfo } + +// GetInfoByNameAndTypes 根据名字和类型获取API +func (s *sSysApi) GetInfoByNameAndTypes(ctx context.Context, name string, types int) (entity *entity.SysApi, err error) { + err = dao.SysApi.Ctx(ctx).Where(g.Map{ + dao.SysApi.Columns().Name: name, + dao.SysApi.Columns().Types: types, + }).Scan(&entity) + return +} + +// ImportApiFile 导入API文件 +func (s *sSysApi) ImportApiFile(ctx context.Context) (err error) { + //获取服务端口号 + port := g.Cfg().MustGet(ctx, "server.address").String() + if port == "" { + err = gerror.New("服务地址不能为空") + return + } + + //获取API路径 + openApiPath := g.Cfg().MustGet(ctx, "server.openapiPath").String() + if openApiPath == "" { + err = gerror.New("openApi路径不能为空") + return + } + + url := "http://127.0.0.1" + port + openApiPath + resp, err := g.Client().Get(ctx, url) + if err != nil { + return + } + respContent := resp.ReadAllString() + status := resp.Status + defer func(resp *gclient.Response) { + if err = resp.Close(); err != nil { + g.Log().Error(ctx, err) + } + }(resp) + if !strings.Contains(status, "200") { + err = gerror.New("请求失败,请联系管理员") + return + } + var apiJsonContent map[string]interface{} + if respContent != "" { + err = json.Unmarshal([]byte(respContent), &apiJsonContent) + if err != nil { + return + } + } + //封装数据、整理入库 + //获取所有的接口 + var address []string + var apiPathsAll = make(map[string][]map[string]string) + paths := apiJsonContent["paths"].(map[string]interface{}) + for key, value := range paths { + //接口信息 + apiInfo := make(map[string]string) + //获取对应的分组 + valueMap := value.(map[string]interface{}) + var valueKey string + for valueMapKey := range valueMap { + if !strings.EqualFold(valueMapKey, "summary") { + valueKey = valueMapKey + break + } + } + apiInfo["address"] = key + address = append(address, key) + //方法 + apiInfo["method"] = valueKey + //名字 + apiInfo["name"] = valueMap[valueKey].(map[string]interface{})["summary"].(string) + + tags := valueMap[valueKey].(map[string]interface{})["tags"].([]interface{}) + fmt.Printf("key:%s---tags:%s\n", key, tags) + for _, tag := range tags { + if _, ok := apiPathsAll[tag.(string)]; !ok { + apiPathsAll[tag.(string)] = []map[string]string{apiInfo} + } else { + apiPathsAll[tag.(string)] = append(apiPathsAll[tag.(string)], apiInfo) + } + } + } + g.Log().Debugf(ctx, "全部API信息:%s", apiPathsAll) + + //获取数据字典类型 + dictData, err := service.DictData().GetDictDataByType(ctx, consts.ApiTypes) + if err != nil { + return err + } + apiTpyes := "IOT" + if dictData.Data != nil && dictData.Values != nil { + for _, value := range dictData.Values { + if strings.EqualFold(value.DictLabel, "sagoo_iot") { + apiTpyes = value.DictValue + } + } + } + err = dao.SysApi.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + //开始入库 + for key, value := range apiPathsAll { + //获取分组ID + var categoryId int + //判断分组是否存在,不存在则创建分组 + categoryInfo, _ := s.GetInfoByNameAndTypes(ctx, key, 1) + if categoryInfo == nil { + //创建分组 + var result sql.Result + result, err = dao.SysApi.Ctx(ctx).Data(do.SysApi{ + ParentId: -1, + Name: key, + Types: 1, + Status: 1, + IsDeleted: 0, + }).Insert() + if err != nil { + return err + } + //获取主键ID + var lastInsertId int64 + lastInsertId, err = service.Sequences().GetSequences(ctx, result, dao.SysApi.Table(), dao.SysApi.Columns().Id) + if err != nil { + return + } + categoryId = int(lastInsertId) + } else { + categoryId = int(categoryInfo.Id) + } + + for _, apiValue := range value { + if strings.Contains(apiValue["address"], "openapi") { + continue + } + //判断接口是否存在 + var sysApi *entity.SysApi + sysApi, err = s.GetInfoByAddress(ctx, apiValue["address"]) + if err != nil { + return + } + if sysApi != nil { + _, err = dao.SysApi.Ctx(ctx).Data(do.SysApi{ + ParentId: categoryId, + Name: apiValue["name"], + Types: 2, + ApiTypes: apiTpyes, + Method: apiValue["method"], + Address: apiValue["address"], + Status: 1, + IsDeleted: 0, + }).Where(dao.SysApi.Columns().Address, apiValue["address"]).Update() + if err != nil { + return err + } + } else { + _, err = dao.SysApi.Ctx(ctx).Data(do.SysApi{ + ParentId: categoryId, + Name: apiValue["name"], + Types: 2, + ApiTypes: apiTpyes, + Method: apiValue["method"], + Address: apiValue["address"], + Status: 1, + IsDeleted: 0, + }).Insert() + if err != nil { + return + } + } + } + } + + //根据地址获取不存在的APIID + var sysApiInfos []*entity.SysApi + err = dao.SysApi.Ctx(ctx).Where(dao.SysApi.Columns().ApiTypes, apiTpyes).WhereNotIn(dao.SysApi.Columns().Address, address).Scan(&sysApiInfos) + if err != nil { + return + } + var apiIds []uint + for _, info := range sysApiInfos { + apiIds = append(apiIds, info.Id) + } + //删除API + _, err = dao.SysApi.Ctx(ctx).Data(g.Map{ + dao.SysApi.Columns().IsDeleted: 1, + dao.SysApi.Columns().Status: 0, + dao.SysApi.Columns().DeletedAt: gtime.Now(), + dao.SysApi.Columns().DeletedBy: service.Context().GetUserId(ctx), + }).WhereIn(dao.SysApi.Columns().Id, apiIds).Update() + + if err != nil { + return + } + //删除于菜单关系绑定 + _, err = dao.SysMenuApi.Ctx(ctx).Data(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 1, + dao.SysMenuApi.Columns().DeletedAt: gtime.Now(), + dao.SysMenuApi.Columns().DeletedBy: service.Context().GetUserId(ctx), + }).WhereIn(dao.SysMenuApi.Columns().Id, apiIds).Update() + if err != nil { + return + } + return + }) + return +} diff --git a/internal/logic/system/sys_authorize.go b/internal/logic/system/sys_authorize.go index c2e8b86..4747aa5 100644 --- a/internal/logic/system/sys_authorize.go +++ b/internal/logic/system/sys_authorize.go @@ -3,19 +3,16 @@ package system import ( "context" "encoding/json" - "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/container/gvar" - "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcache" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" "strings" ) @@ -167,19 +164,17 @@ func (s *sSysAuthorize) GetInfoByRoleId(ctx context.Context, roleId int) (data [ // GetInfoByRoleIds 根据角色ID数组获取权限信息 func (s *sSysAuthorize) GetInfoByRoleIds(ctx context.Context, roleIds []int) (data []*entity.SysAuthorize, err error) { - cache := common.Cache() //获取缓存菜单按钮信息 for _, v := range roleIds { var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysAuthorize+"_"+gconv.String(v)) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysAuthorize+"_"+gconv.String(v)) if err != nil { return } - if tmpData != nil { + if tmpData.Val() != nil { var sysAuthorizeInfo []*entity.SysAuthorize - json.Unmarshal([]byte(tmpData.Val().(string)), &sysAuthorizeInfo) + err = json.Unmarshal([]byte(tmpData.Val().(string)), &sysAuthorizeInfo) data = append(data, sysAuthorizeInfo...) - return } } if data == nil && len(data) == 0 { @@ -210,11 +205,47 @@ func (s *sSysAuthorize) DelByRoleId(ctx context.Context, roleId int) (err error) } func (s *sSysAuthorize) Add(ctx context.Context, authorize []*entity.SysAuthorize) (err error) { - _, err = dao.SysAuthorize.Ctx(ctx).Data(authorize).Insert() + var input []*model.SysAuthorizeInput + if err = gconv.Scan(authorize, &input); err != nil { + return + } + _, err = dao.SysAuthorize.Ctx(ctx).Data(input).Insert() return } func (s *sSysAuthorize) AddAuthorize(ctx context.Context, roleId int, menuIds []string, buttonIds []string, columnIds []string, apiIds []string) (err error) { + //判断是否启用安全控制 + var configDataByIsSecurityControlEnabled *entity.SysConfig + configDataByIsSecurityControlEnabled, err = service.ConfigData().GetConfigByKey(ctx, consts.SysIsSecurityControlEnabled) + if err != nil { + return + } + sysColumnSwitch := 0 + sysButtonSwitch := 0 + sysApiSwitch := 0 + + if configDataByIsSecurityControlEnabled != nil && strings.EqualFold(configDataByIsSecurityControlEnabled.ConfigValue, "1") { + //获取系统列表开关参数 + var sysColumnSwitchConfig *entity.SysConfig + sysColumnSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, consts.SysColumnSwitch) + if sysColumnSwitchConfig != nil { + sysColumnSwitch = gconv.Int(sysColumnSwitchConfig.ConfigValue) + } + //获取系统按钮开关参数 + var sysButtonSwitchConfig *entity.SysConfig + sysButtonSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, consts.SysButtonSwitch) + if sysButtonSwitchConfig != nil { + sysButtonSwitch = gconv.Int(sysButtonSwitchConfig.ConfigValue) + } + + //获取系统API开关参数 + var sysApiSwitchConfig *entity.SysConfig + sysApiSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, consts.SysApiSwitch) + if sysApiSwitchConfig != nil { + sysApiSwitch = gconv.Int(sysApiSwitchConfig.ConfigValue) + } + } + err = g.Try(ctx, func(ctx context.Context) { //删除原有权限 err = service.SysAuthorize().DelByRoleId(ctx, roleId) @@ -240,69 +271,81 @@ func (s *sSysAuthorize) AddAuthorize(ctx context.Context, roleId int, menuIds [] return } //封装按钮权限 - for _, id := range buttonIds { - var authorize = new(entity.SysAuthorize) - split := strings.Split(id, "_") - if len(split) < 2 { - isTrue = false - break + if sysButtonSwitch == 1 { + for _, id := range buttonIds { + var authorize = new(entity.SysAuthorize) + split := strings.Split(id, "_") + if len(split) < 2 { + isTrue = false + break + } + authorize.ItemsId = gconv.Int(split[0]) + authorize.ItemsType = consts.Button + authorize.RoleId = roleId + authorize.IsCheckAll = gconv.Int(split[1]) + authorize.IsDeleted = 0 + authorizeInfo = append(authorizeInfo, authorize) + } + if !isTrue { + err = gerror.New("按钮权限参数错误") + return } - authorize.ItemsId = gconv.Int(split[0]) - authorize.ItemsType = consts.Button - authorize.RoleId = roleId - authorize.IsCheckAll = gconv.Int(split[1]) - authorize.IsDeleted = 0 - authorizeInfo = append(authorizeInfo, authorize) - } - if !isTrue { - err = gerror.New("按钮权限参数错误") - return } + //封装列表权限 - for _, id := range columnIds { - var authorize = new(entity.SysAuthorize) - split := strings.Split(id, "_") - if len(split) < 2 { - isTrue = false - break + if sysColumnSwitch == 1 { + for _, id := range columnIds { + var authorize = new(entity.SysAuthorize) + split := strings.Split(id, "_") + if len(split) < 2 { + isTrue = false + break + } + authorize.ItemsId = gconv.Int(split[0]) + authorize.ItemsType = consts.Column + authorize.RoleId = roleId + authorize.IsCheckAll = gconv.Int(split[1]) + authorize.IsDeleted = 0 + authorizeInfo = append(authorizeInfo, authorize) + } + if !isTrue { + err = gerror.New("列表权限参数错误") + return } - authorize.ItemsId = gconv.Int(split[0]) - authorize.ItemsType = consts.Column - authorize.RoleId = roleId - authorize.IsCheckAll = gconv.Int(split[1]) - authorize.IsDeleted = 0 - authorizeInfo = append(authorizeInfo, authorize) - } - if !isTrue { - err = gerror.New("列表权限参数错误") - return } + //封装接口权限 - for _, id := range apiIds { - var authorize = new(entity.SysAuthorize) - split := strings.Split(id, "_") - if len(split) < 2 { - isTrue = false - break + if sysApiSwitch == 1 { + for _, id := range apiIds { + var authorize = new(entity.SysAuthorize) + split := strings.Split(id, "_") + if len(split) < 2 { + isTrue = false + break + } + authorize.ItemsId = gconv.Int(split[0]) + authorize.ItemsType = consts.Api + authorize.RoleId = roleId + authorize.IsCheckAll = gconv.Int(split[1]) + authorize.IsDeleted = 0 + authorizeInfo = append(authorizeInfo, authorize) + } + if !isTrue { + err = gerror.New("接口权限参数错误") + return } - authorize.ItemsId = gconv.Int(split[0]) - authorize.ItemsType = consts.Api - authorize.RoleId = roleId - authorize.IsCheckAll = gconv.Int(split[1]) - authorize.IsDeleted = 0 - authorizeInfo = append(authorizeInfo, authorize) - } - if !isTrue { - err = gerror.New("接口权限参数错误") - return } + err = s.Add(ctx, authorizeInfo) if err != nil { err = gerror.New("添加权限失败") return } //添加缓存信息 - _, err = gcache.SetIfNotExist(ctx, consts.CacheSysAuthorize+"_"+gconv.String(roleId), authorizeInfo, 0) + err := cache.Instance().Set(ctx, consts.CacheSysAuthorize+"_"+gconv.String(roleId), authorizeInfo, 0) + if err != nil { + return + } }) return } @@ -373,6 +416,30 @@ func (s *sSysAuthorize) IsAllowAuthorize(ctx context.Context, roleId int) (isAll if nowUserAuthorizeErr != nil { return } + + //获取系统列表开关参数 + var sysColumnSwitchConfig *entity.SysConfig + sysColumnSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, "sys.column.switch") + sysColumnSwitch := 0 + if sysColumnSwitchConfig != nil { + sysColumnSwitch = gconv.Int(sysColumnSwitchConfig.ConfigValue) + } + //获取系统按钮开关参数 + var sysButtonSwitchConfig *entity.SysConfig + sysButtonSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, "sys.button.switch") + sysButtonSwitch := 0 + if sysButtonSwitchConfig != nil { + sysButtonSwitch = gconv.Int(sysButtonSwitchConfig.ConfigValue) + } + + //获取系统API开关参数 + var sysApiSwitchConfig *entity.SysConfig + sysApiSwitchConfig, err = service.ConfigData().GetConfigByKey(ctx, "sys.api.switch") + sysApiSwitch := 0 + if sysApiSwitchConfig != nil { + sysApiSwitch = gconv.Int(sysApiSwitchConfig.ConfigValue) + } + //菜单Ids var nowUserMenuIds []int //按钮Ids @@ -384,14 +451,61 @@ func (s *sSysAuthorize) IsAllowAuthorize(ctx context.Context, roleId int) (isAll for _, authorize := range nowUserAuthorizeInfo { if strings.EqualFold(authorize.ItemsType, consts.Menu) { nowUserMenuIds = append(nowUserMenuIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Button) { + } else if strings.EqualFold(authorize.ItemsType, consts.Button) && sysButtonSwitch == 1 { nowUserMenuButtonIds = append(nowUserMenuButtonIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Column) { + } else if strings.EqualFold(authorize.ItemsType, consts.Column) && sysColumnSwitch == 1 { nowUserMenuColumnIds = append(nowUserMenuColumnIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Api) { + } else if strings.EqualFold(authorize.ItemsType, consts.Api) && sysApiSwitch == 1 { nowUserMenuApiIds = append(nowUserMenuApiIds, authorize.ItemsId) } } + + //判断按钮、列表、API开关状态,如果关闭则获取菜单对应的所有信息 + //判断按钮开关 + if sysButtonSwitch == 0 { + //获取所有按钮 + var menuButtons []*entity.SysMenuButton + menuButtons, err = service.SysMenuButton().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuButtons) > 0 { + for _, menuButton := range menuButtons { + nowUserMenuButtonIds = append(nowUserMenuButtonIds, int(menuButton.Id)) + } + } + } + + //判断列表开关 + if sysColumnSwitch == 0 { + //获取所有列表 + var menuColumns []*entity.SysMenuColumn + menuColumns, err = service.SysMenuColumn().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuColumns) > 0 { + for _, menuColumn := range menuColumns { + nowUserMenuColumnIds = append(nowUserMenuColumnIds, int(menuColumn.Id)) + } + } + } + + //判断API开关 + if sysApiSwitch == 0 { + //获取所有API + var menuApis []*entity.SysMenuApi + menuApis, err = service.SysMenuApi().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuApis) > 0 { + for _, menuApi := range menuApis { + nowUserMenuApiIds = append(nowUserMenuApiIds, int(menuApi.Id)) + } + } + } + //判断当前登录用户是否大于授权角色的权限 //获取当前登录用户的菜单信息 nowUserMenuInfo, _ := service.SysMenu().GetInfoByMenuIds(ctx, nowUserMenuIds) @@ -482,8 +596,6 @@ func (s *sSysAuthorize) IsAllowAuthorize(ctx context.Context, roleId int) (isAll // InitAuthorize 初始化系统权限 func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { - cache := common.Cache() - //获取所有菜单信息 menuInfos, err := service.SysMenu().GetAll(ctx) if err != nil { @@ -495,19 +607,23 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { for _, menuInfo := range menuInfos { menuIds = append(menuIds, menuInfo.Id) } - //根据菜单ID所有按钮信息 + //获取所有的按钮列表 var menuButtonInfos []*entity.SysMenuButton err = dao.SysMenuButton.Ctx(ctx).Where(g.Map{ dao.SysMenuButton.Columns().IsDeleted: 0, dao.SysMenuButton.Columns().Status: 1, }).Scan(&menuButtonInfos) if err != nil { + g.Log().Debug(ctx, "获取菜单按钮信息失败", err.Error()) return } if menuButtonInfos != nil && len(menuButtonInfos) > 0 { - cache.Set(ctx, consts.CacheSysMenuButton, menuButtonInfos, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuButton, menuButtonInfos, 0) + if err != nil { + return + } } - //根据菜单ID获取所有列表信息 + //获取所有的菜单列表 var menuColumnInfos []*entity.SysMenuColumn err = dao.SysMenuColumn.Ctx(ctx).Where(g.Map{ dao.SysMenuColumn.Columns().IsDeleted: 0, @@ -517,9 +633,12 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { return } if menuColumnInfos != nil && len(menuColumnInfos) > 0 { - cache.Set(ctx, consts.CacheSysMenuColumn, menuColumnInfos, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuColumn, menuColumnInfos, 0) + if err != nil { + return + } } - //根据菜单ID获取绑定的所有接口ID + //获取所有的菜单接口 var menuApiInfos []*entity.SysMenuApi err = dao.SysMenuApi.Ctx(ctx).Where(g.Map{ dao.SysMenuApi.Columns().IsDeleted: 0, @@ -528,7 +647,10 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { return } if menuApiInfos != nil && len(menuApiInfos) > 0 { - cache.Set(ctx, consts.CacheSysMenuApi, menuApiInfos, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuApi, menuApiInfos, 0) + if err != nil { + return + } } //添加缓存信息 for _, menuId := range menuIds { @@ -540,7 +662,10 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { } //添加按钮缓存 if tmpMenuButton != nil && len(tmpMenuButton) > 0 { - cache.Set(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId), tmpMenuButton, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId), tmpMenuButton, 0) + if err != nil { + return + } } var tmpMenuColumn []*entity.SysMenuColumn @@ -551,7 +676,10 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { } //添加列表缓存 if tmpMenuColumn != nil && len(tmpMenuColumn) > 0 { - cache.Set(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId), tmpMenuColumn, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId), tmpMenuColumn, 0) + if err != nil { + return + } } var tmpMenuApi []*entity.SysMenuApi @@ -562,7 +690,10 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { } //添加菜单与接口绑定关系缓存 if tmpMenuApi != nil && len(tmpMenuApi) > 0 { - cache.Set(ctx, consts.CacheSysMenuApi+"_"+gconv.String(menuId), tmpMenuApi, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuApi+"_"+gconv.String(menuId), tmpMenuApi, 0) + if err != nil { + return + } } } //获取所有的接口信息 @@ -575,19 +706,22 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { return } if sysApiInfos != nil && len(sysApiInfos) > 0 { - cache.Set(ctx, consts.CacheSysApi, sysApiInfos, 0) + err = cache.Instance().Set(ctx, consts.CacheSysApi, sysApiInfos, 0) + if err != nil { + return + } } //获取所有的角色ID var roleInfos []*entity.SysRole - dao.SysRole.Ctx(ctx).Where(g.Map{ + err = dao.SysRole.Ctx(ctx).Where(g.Map{ dao.SysRole.Columns().IsDeleted: 0, dao.SysRole.Columns().Status: 1, }).Scan(&roleInfos) if roleInfos != nil && len(roleInfos) > 0 { //获取所有的权限配置 var authorizeInfos []*entity.SysAuthorize - dao.SysAuthorize.Ctx(ctx).Where(g.Map{ + err = dao.SysAuthorize.Ctx(ctx).Where(g.Map{ dao.SysAuthorize.Columns().IsDeleted: 0, }).Scan(&authorizeInfos) @@ -599,156 +733,13 @@ func (s *sSysAuthorize) InitAuthorize(ctx context.Context) (err error) { } } if tmpAuthorizeInfos != nil && len(tmpAuthorizeInfos) > 0 { - cache.Set(ctx, consts.CacheSysAuthorize+"_"+gconv.String(roleInfo.Id), tmpAuthorizeInfos, 0) - } - } - } - } - return -} - -// FilterDataByPermissions 根据数据权限过滤数据 -func (s *sSysAuthorize) FilterDataByPermissions(ctx context.Context, model *gdb.Model) (*gdb.Model, error) { - where, err := service.SysAuthorize().GetDataWhere(ctx) - if err != nil { - return model, err - } - if where != nil && len(where) > 0 { - return model.Where(where), err - } - return model, err -} - -// GetDataWhere 获取数据权限条件查询 -func (s *sSysAuthorize) GetDataWhere(ctx context.Context) (where g.Map, err error) { - //判断请求方式 - requestWay := service.Context().GetRequestWay(ctx) - if strings.EqualFold(requestWay, consts.TokenAuth) { - loginUserId := service.Context().GetUserId(ctx) - loginUserDeptId := service.Context().GetUserDeptId(ctx) - //1、获取当前用户所属角色 - userRoleInfo, userRoleErr := service.SysUserRole().GetInfoByUserId(ctx, loginUserId) - if userRoleErr != nil { - err = gerror.New("获取用户角色失败") - return - } - if userRoleInfo == nil { - err = gerror.New("用户无权限访问") - return - } - //判断用户是否为超级管理员 - var isSuperAdmin = false - var roleIds []int - for _, userRole := range userRoleInfo { - if userRole.RoleId == 1 { - isSuperAdmin = true - } - roleIds = append(roleIds, userRole.RoleId) - } - if isSuperAdmin { - //超级管理员可以访问所有的数据 - return - } - //不是超级管理员则获取所有角色信息 - var roleInfo []*entity.SysRole - err = dao.SysRole.Ctx(ctx).WhereIn(dao.SysRole.Columns().Id, roleIds).Where(g.Map{ - dao.SysRole.Columns().Status: 1, - dao.SysRole.Columns().IsDeleted: 0, - }).Scan(&roleInfo) - if err != nil { - err = gerror.New("获取用户角色失败") - return - } - //2获取角色对应数据权限 - deptIdArr := gset.New() - for _, role := range roleInfo { - switch role.DataScope { - case 1: //全部数据权限 - return - case 2: //自定数据权限 - //获取角色所有的部门信息 - roleDeptInfo, _ := service.SysRoleDept().GetInfoByRoleId(ctx, int(role.Id)) - if roleDeptInfo == nil { - err = gerror.New(role.Name + "自定义数据范围,请先配置部门!") - return - } - var deptIds []int64 - for _, roleDept := range roleDeptInfo { - deptIds = append(deptIds, roleDept.DeptId) - } - deptIdArr.Add(gconv.Interfaces(deptIds)...) - case 3: //本部门数据权限 - deptIdArr.Add(gconv.Int64(loginUserDeptId)) - case 4: //本部门及以下数据权限 - deptIdArr.Add(gconv.Int64(loginUserDeptId)) - //获取所有部门 - var deptInfo []*entity.SysDept - m := dao.SysDept.Ctx(ctx) - _ = m.Where(g.Map{ - dao.SysDept.Columns().Status: 1, - dao.SysDept.Columns().IsDeleted: 0, - }).Scan(&deptInfo) - - if deptInfo != nil { - //获取当前部门所有的下级部门信息 - childrenDeptInfo := GetNextDeptInfoByNowDeptId(int64(loginUserDeptId), deptInfo) - if childrenDeptInfo != nil { - allChildrenDeptInfo := GetAllNextDeptInfoByChildrenDept(childrenDeptInfo, deptInfo, childrenDeptInfo) - if allChildrenDeptInfo != nil { - for _, allChildrenDept := range allChildrenDeptInfo { - deptIdArr.Add(gconv.Int64(allChildrenDept.DeptId)) - } - } - } - } - case 5: //仅限于自己的数据 - where = g.Map{"created_by": loginUserId} - return - } - } - //此添加是为了兼容以前的数据 - deptIdArr.Add(0) - if deptIdArr.Size() > 0 { - where = g.Map{"dept_id": deptIdArr.Slice()} - } - } else if strings.EqualFold(requestWay, consts.AKSK) { - //判断是父级部门还是子部门 - deptIdArr := gset.New() - parentDeptId := service.Context().GetUserDeptId(ctx) - if parentDeptId != 0 { - deptIdArr.Add(gconv.Int64(parentDeptId)) - //获取所有部门 - var deptInfo []*entity.SysDept - m := dao.SysDept.Ctx(ctx) - _ = m.Where(g.Map{ - dao.SysDept.Columns().Status: 1, - dao.SysDept.Columns().IsDeleted: 0, - }).Scan(&deptInfo) - if deptInfo != nil { - //获取当前部门所有的下级部门信息 - childrenDeptInfo := GetNextDeptInfoByNowDeptId(int64(parentDeptId), deptInfo) - if childrenDeptInfo != nil { - allChildrenDeptInfo := GetAllNextDeptInfoByChildrenDept(childrenDeptInfo, deptInfo, childrenDeptInfo) - if allChildrenDeptInfo != nil { - for _, allChildrenDept := range allChildrenDeptInfo { - deptIdArr.Add(gconv.Int64(allChildrenDept.DeptId)) - } + err = cache.Instance().Set(ctx, consts.CacheSysAuthorize+"_"+gconv.String(roleInfo.Id), tmpAuthorizeInfos, 0) + if err != nil { + return } } } - } else { - //获取传过来的子部门ID - childrenDeptIds := service.Context().GetChildrenDeptId(ctx) - for _, childrenDeptId := range childrenDeptIds { - deptIdArr.Add(gconv.Int64(childrenDeptId)) - } - } - //此添加是为了兼容以前的数据 - deptIdArr.Add(0) - if deptIdArr.Size() > 0 { - where = g.Map{"dept_id": deptIdArr.Slice()} } } - return } diff --git a/internal/logic/system/sys_authorize_utils.go b/internal/logic/system/sys_authorize_utils.go index 68bc419..f9fd7f5 100644 --- a/internal/logic/system/sys_authorize_utils.go +++ b/internal/logic/system/sys_authorize_utils.go @@ -3,16 +3,16 @@ package system import ( "context" "encoding/json" - "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "reflect" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sort" "strings" ) @@ -61,8 +61,8 @@ func GetAllAuthorizeQueryParentTree(childrenMenuTreeRes []*model.AuthorizeQueryT } // GetRoleTree 获取角色树 -func GetRoleTree(roleInfo []*entity.SysRole) (dataTree []*model.RoleTreeOut, err error) { - var parentNodeRes []*model.RoleTreeOut +func GetRoleTree(ctx context.Context, roleInfo []*entity.SysRole) (dataTree []*model.RoleTreeOut, err error) { + var parentNodeOut []*model.RoleTreeOut if roleInfo != nil { //获取所有的根节点 for _, v := range roleInfo { @@ -71,11 +71,43 @@ func GetRoleTree(roleInfo []*entity.SysRole) (dataTree []*model.RoleTreeOut, err if err = gconv.Scan(v, &parentNode); err != nil { return } - parentNodeRes = append(parentNodeRes, parentNode) + + var isExist = false + for _, roleOut := range parentNodeOut { + if roleOut.Id == parentNode.Id { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } + } else { + //查找根节点 + parentRole := FindRoleParentByChildrenId(ctx, v.ParentId) + if err = gconv.Scan(parentRole, &parentNode); err != nil { + return + } + var isExist = false + for _, roleOut := range parentNodeOut { + if roleOut.Id == parentRole.Id { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } } } } - treeData := RoleTree(parentNodeRes, roleInfo) + + //对父节点进行排序 + sort.SliceStable(parentNodeOut, func(i, j int) bool { + return parentNodeOut[i].ListOrder < parentNodeOut[j].ListOrder + }) + + treeData := RoleTree(parentNodeOut, roleInfo) return treeData, nil } @@ -93,12 +125,30 @@ func RoleTree(parentNodeRes []*model.RoleTreeOut, data []*entity.SysRole) (dataT parentNodeRes[k].Children = append(parentNodeRes[k].Children, node) } } + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].ListOrder < v.Children[j].ListOrder + }) RoleTree(v.Children, data) } return parentNodeRes } -func GetApiTree(apiInfo []*entity.SysApi) (dataTree []*model.SysApiTreeOut, err error) { +// FindRoleParentByChildrenId 根据子节点获取角色根节点 +func FindRoleParentByChildrenId(ctx context.Context, parentId int) *entity.SysRole { + var role *entity.SysRole + + _ = dao.SysRole.Ctx(ctx).Where(g.Map{ + dao.SysRole.Columns().Id: parentId, + }).Scan(&role) + + if role.ParentId != -1 { + return FindRoleParentByChildrenId(ctx, role.ParentId) + } + return role +} + +func GetApiTree(apiInfo []*model.SysApiTreeOut) (dataTree []*model.SysApiTreeOut, err error) { var parentNodeRes []*model.SysApiTreeOut if apiInfo != nil { //获取所有的根节点 @@ -122,7 +172,7 @@ func GetApiTree(apiInfo []*entity.SysApi) (dataTree []*model.SysApiTreeOut, err } // ApiTree 生成接口树结构 -func ApiTree(parentNodeRes []*model.SysApiTreeOut, data []*entity.SysApi) (dataTree []*model.SysApiTreeOut) { +func ApiTree(parentNodeRes []*model.SysApiTreeOut, data []*model.SysApiTreeOut) (dataTree []*model.SysApiTreeOut) { //循环所有一级菜单 for k, v := range parentNodeRes { //查询所有该菜单下的所有子菜单 @@ -142,12 +192,13 @@ func ApiTree(parentNodeRes []*model.SysApiTreeOut, data []*entity.SysApi) (dataT // GetMenuInfo 根据菜单ID获取指定菜单信息或者获取所有菜单信息 func GetMenuInfo(ctx context.Context, menuIds []int) (userMenuTreeOut []*model.UserMenuTreeOut, err error) { - cache := common.Cache() //查看REDIS是否存在 - tmpData := cache.Get(ctx, consts.CacheSysMenu) + tmpData, err := cache.Instance().Get(ctx, consts.CacheSysMenu) //将缓存菜单转为struct var tmpMenuInfo []*entity.SysMenu - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpMenuInfo) + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpMenuInfo); err != nil { + return + } var menuInfo []*entity.SysMenu if menuIds != nil { @@ -381,7 +432,17 @@ func GetAuthorizeItemsTypeTreeOut(ctx context.Context, menuIds []int, itemsType //获取相关接口ID var apiIds []int for _, menuApi := range menuApiInfo { - apiIds = append(apiIds, menuApi.ApiId) + var isExits = false + for _, apiId := range apiIds { + if apiId == menuApi.ApiId { + isExits = true + break + } + } + if !isExits { + apiIds = append(apiIds, menuApi.ApiId) + } + } //获取相关接口信息 @@ -393,24 +454,27 @@ func GetAuthorizeItemsTypeTreeOut(ctx context.Context, menuIds []int, itemsType if err = gconv.Scan(apiInfo, &apiInfoOut); err != nil { return } + + var childrenApiMap []g.Map for _, menuApi := range menuApiInfo { if menuApi.MenuId == int(menu.Id) { for _, api := range apiInfoOut { if menuApi.ApiId == api.Id { + var childrenMap g.Map //菜单与接口绑定ID api.Id = int(menuApi.Id) //接口ID api.ApiId = api.Id api.Title = api.Name - var childrenMap g.Map if err = gconv.Scan(api, &childrenMap); err != nil { return } - menu.Children = append(menu.Children, childrenMap) + childrenApiMap = append(childrenApiMap, childrenMap) } } } } + menu.Children = append(menu.Children, childrenApiMap...) } } @@ -432,6 +496,10 @@ func GetUserMenuTree(userMenuTreeRes []*model.UserMenuTreeOut) (dataTree []*mode } } } + //对父节点进行排序 + sort.SliceStable(userMenuParentNodeTreeRes, func(i, j int) bool { + return userMenuParentNodeTreeRes[i].Weigh > userMenuParentNodeTreeRes[j].Weigh + }) treeData := UserMenuTree(userMenuParentNodeTreeRes, userMenuTreeRes) return treeData } @@ -446,6 +514,10 @@ func UserMenuTree(userMenuParentNodeTreeRes []*model.UserMenuTreeOut, data []*mo userMenuParentNodeTreeRes[k].Children = append(userMenuParentNodeTreeRes[k].Children, j) } } + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].Weigh > v.Children[j].Weigh + }) UserMenuTree(v.Children, data) } return userMenuParentNodeTreeRes @@ -547,108 +619,6 @@ func UserMenuColumnTree(parentMenuColumnNodeRes []*model.UserMenuColumnOut, data return parentMenuColumnNodeRes } -// GetDataWhere 获取数据权限条件查询 -func GetDataWhere(ctx context.Context, loginUserId int, entity interface{}) (where g.Map, err error) { - //获取当前登录用户信息 - userInfo, err := service.SysUser().GetUserById(ctx, uint(loginUserId)) - if err != nil { - err = gerror.New("登录用户信息错误") - return - } - if userInfo == nil { - err = gerror.New("登录用户不存在无法访问") - return - } - if userInfo != nil && userInfo.Status == 0 { - err = gerror.New("登录用户已禁用无法访问") - return - } - if userInfo != nil && userInfo.Status == 2 { - err = gerror.New("登录用户未验证无法访问") - return - } - t := reflect.TypeOf(entity) - for i := 0; i < t.Elem().NumField(); i++ { - if t.Elem().Field(i).Name == "CreatedBy" { - //若存在用户id的字段,则生成判断数据权限的条件 - //1、获取当前用户所属角色 - userRoleInfo, userRoleErr := service.SysUserRole().GetInfoByUserId(ctx, loginUserId) - if userRoleErr != nil { - err = gerror.New("获取用户角色失败") - return - } - if userRoleInfo == nil { - err = gerror.New("用户无权限访问") - return - } - //判断用户是否为超级管理员 - var isSuperAdmin = false - var roleIds []int - for _, userRole := range userRoleInfo { - if userRole.RoleId == 1 { - isSuperAdmin = true - } - roleIds = append(roleIds, userRole.RoleId) - } - if isSuperAdmin { - //超级管理员可以访问所有的数据 - return - } - //不是超级管理员则获取所有角色信息 - roleInfo, roleInfoErr := service.SysRole().GetInfoByIds(ctx, roleIds) - if roleInfoErr != nil { - err = gerror.New("获取用户角色失败") - return - } - //2获取角色对应数据权限 - deptIdArr := gset.New() - for _, role := range roleInfo { - switch role.DataScope { - case 1: //全部数据权限 - return - case 2: //自定数据权限 - //获取角色所有的部门信息 - roleDeptInfo, _ := service.SysRoleDept().GetInfoByRoleId(ctx, int(role.Id)) - if roleDeptInfo == nil { - err = gerror.New(role.Name + "自定义数据范围,请先配置部门!") - return - } - var deptIds []int64 - for _, roleDept := range roleDeptInfo { - deptIds = append(deptIds, roleDept.DeptId) - } - deptIdArr.Add(gconv.Interfaces(deptIds)...) - case 3: //本部门数据权限 - deptIdArr.Add(gconv.Int64(userInfo.DeptId)) - case 4: //本部门及以下数据权限 - deptIdArr.Add(gconv.Int64(userInfo.DeptId)) - //获取所有的部门信息 - deptInfo, _ := service.SysDept().GetAll(ctx) - if deptInfo != nil { - //获取当前部门所有的下级部门信息 - childrenDeptInfo := GetNextDeptInfoByNowDeptId(int64(userInfo.DeptId), deptInfo) - if childrenDeptInfo != nil { - allChildrenDeptInfo := GetAllNextDeptInfoByChildrenDept(childrenDeptInfo, deptInfo, childrenDeptInfo) - if allChildrenDeptInfo != nil { - for _, allChildrenDept := range allChildrenDeptInfo { - deptIdArr.Add(gconv.Int64(allChildrenDept.DeptId)) - } - } - } - } - case 5: //仅限于自己的数据 - where = g.Map{"created_by": userInfo.Id} - return - } - } - if deptIdArr.Size() > 0 { - where = g.Map{"dept_id": deptIdArr.Slice()} - } - } - } - return -} - // GetNextDeptInfoByNowDeptId 获取当前部门ID下一层级的部门信息 func GetNextDeptInfoByNowDeptId(id int64, deptInfo []*entity.SysDept) (data []*entity.SysDept) { //循环所有的部门信息 diff --git a/internal/logic/system/sys_certificate.go b/internal/logic/system/sys_certificate.go new file mode 100644 index 0000000..6af7bae --- /dev/null +++ b/internal/logic/system/sys_certificate.go @@ -0,0 +1,182 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "strings" +) + +type sSysCertificate struct{} + +func sSysCertificateNew() *sSysCertificate { + return &sSysCertificate{} +} +func init() { + service.RegisterSysCertificate(sSysCertificateNew()) +} + +// GetList 获取列表数据 +func (s *sSysCertificate) GetList(ctx context.Context, input *model.SysCertificateListInput) (total, page int, out []*model.SysCertificateListOut, err error) { + m := dao.SysCertificate.Ctx(ctx) + + if input.Name != "" { + m = m.WhereLike(dao.SysCertificate.Columns().Name, "%"+input.Name+"%") + } + if input.Status != -1 { + m = m.Where(dao.SysCertificate.Columns().Status, input.Status) + } + + total, err = m.Count() + if err != nil { + err = gerror.New("获取总行数失败") + return + } + page = input.PageNum + if input.PageSize == 0 { + input.PageSize = consts.PageSize + } + err = m.Page(page, input.PageSize).OrderDesc(dao.SysCertificate.Columns().CreatedAt).Scan(&out) + if err != nil { + err = gerror.New("获取数据失败") + } + return +} + +// GetInfoById 获取指定ID数据 +func (s *sSysCertificate) GetInfoById(ctx context.Context, id int) (out *model.SysCertificateListOut, err error) { + err = dao.SysCertificate.Ctx(ctx).Where(dao.SysCertificate.Columns().Id, id).Scan(&out) + return +} + +// Add 添加数据 +func (s *sSysCertificate) Add(ctx context.Context, input *model.AddSysCertificateListInput) (err error) { + if strings.TrimSpace(input.Name) == "" { + err = gerror.New("名称不能为空") + return + } + num, _ := dao.SysCertificate.Ctx(ctx).Where(g.Map{ + dao.SysCertificate.Columns().Name: input.Name, + dao.SysCertificate.Columns().IsDeleted: 0, + }).Count() + if num > 0 { + err = gerror.New("证书已存在,无法重复添加") + } + var sysCertificate *entity.SysCertificate + if err = gconv.Scan(input, &sysCertificate); err != nil { + return + } + loginUserId := service.Context().GetUserId(ctx) + /*sysCertificate.Status = 0 + sysCertificate.IsDeleted = 0 + sysCertificate.CreatedAt = gtime.Now() + sysCertificate.CreatedBy = uint(loginUserId)*/ + _, err = dao.SysCertificate.Ctx(ctx).Data(do.SysCertificate{ + DeptId: service.Context().GetUserDeptId(ctx), + Name: sysCertificate.Name, + Standard: sysCertificate.Standard, + FileContent: sysCertificate.FileContent, + PublicKeyContent: sysCertificate.PublicKeyContent, + PrivateKeyContent: sysCertificate.PrivateKeyContent, + Description: sysCertificate.Description, + Status: 0, + IsDeleted: 0, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() + return +} + +// Edit 修改数据 +func (s *sSysCertificate) Edit(ctx context.Context, input *model.EditSysCertificateListInput) (err error) { + if strings.TrimSpace(input.Name) == "" { + err = gerror.New("名称不能为空") + return + } + var sysCertificate *entity.SysCertificate + err = dao.SysCertificate.Ctx(ctx).Where(g.Map{ + dao.SysCertificate.Columns().Id: input.Id, + }).Scan(&sysCertificate) + + if sysCertificate == nil { + err = gerror.New("ID错误") + } + + if sysCertificate.IsDeleted == 1 { + err = gerror.New("已删除,无法更新") + } + sysCertificate.Name = input.Name + sysCertificate.Standard = input.Standard + sysCertificate.FileContent = input.FileContent + sysCertificate.PublicKeyContent = input.PublicKeyContent + sysCertificate.PrivateKeyContent = input.PrivateKeyContent + sysCertificate.Description = input.Description + loginUserId := service.Context().GetUserId(ctx) + sysCertificate.UpdatedBy = loginUserId + sysCertificate.UpdatedAt = gtime.Now() + _, err = dao.SysCertificate.Ctx(ctx).Data(sysCertificate).Where(dao.SysCertificate.Columns().Id, sysCertificate.Id).Update() + return +} + +// Delete 删除数据 +func (s *sSysCertificate) Delete(ctx context.Context, id int) (err error) { + var sysCertificate *entity.SysCertificate + err = dao.SysCertificate.Ctx(ctx).Where(g.Map{ + dao.SysCertificate.Columns().Id: id, + }).Scan(&sysCertificate) + + if sysCertificate == nil { + err = gerror.New("ID错误") + } + if sysCertificate.IsDeleted == 1 { + err = gerror.New("无法重复删除") + } + + loginUserId := service.Context().GetUserId(ctx) + sysCertificate.IsDeleted = 1 + sysCertificate.DeletedBy = loginUserId + sysCertificate.DeletedAt = gtime.Now() + _, err = dao.SysCertificate.Ctx(ctx).Data(sysCertificate).Where(dao.SysCertificate.Columns().Id, id).Update() + return +} + +// EditStatus 更新状态 +func (s *sSysCertificate) EditStatus(ctx context.Context, id int, status int) (err error) { + var sysCertificate *entity.SysCertificate + err = dao.SysCertificate.Ctx(ctx).Where(g.Map{ + dao.SysCertificate.Columns().Id: id, + }).Scan(&sysCertificate) + + if sysCertificate == nil { + err = gerror.New("ID错误") + } + if sysCertificate.Status == status { + err = gerror.New("已更新,无法重复更新") + } + + loginUserId := service.Context().GetUserId(ctx) + sysCertificate.Status = status + sysCertificate.UpdatedBy = loginUserId + sysCertificate.UpdatedAt = gtime.Now() + _, err = dao.SysCertificate.Ctx(ctx).Data(sysCertificate).Where(dao.SysCertificate.Columns().Id, id).Update() + return +} + +// GetAll 获取所有证书 +func (s *sSysCertificate) GetAll(ctx context.Context) (out []*entity.SysCertificate, err error) { + m := dao.SysCertificate.Ctx(ctx) + + err = m.Where(g.Map{ + dao.SysCertificate.Columns().Status: 1, + dao.SysCertificate.Columns().IsDeleted: 0, + }).Scan(&out) + return +} diff --git a/internal/logic/system/sys_dept.go b/internal/logic/system/sys_dept.go index 7c1f97c..9281275 100644 --- a/internal/logic/system/sys_dept.go +++ b/internal/logic/system/sys_dept.go @@ -2,13 +2,15 @@ package system import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sort" "strconv" "strings" @@ -41,10 +43,41 @@ func (s *sSysDept) GetTree(ctx context.Context, deptName string, status int) (ou if err = gconv.Scan(v, &parentNode); err != nil { return } - parentNodeOut = append(parentNodeOut, parentNode) + var isExist = false + for _, deptOut := range parentNodeOut { + if deptOut.DeptId == parentNode.DeptId { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } + + } else { + //查找根节点 + parentDept := FindDeptParentByChildrenId(ctx, v.ParentId) + if err = gconv.Scan(parentDept, &parentNode); err != nil { + return + } + var isExist = false + for _, deptOut := range parentNodeOut { + if deptOut.DeptId == parentDept.DeptId { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } } } } + + //对父节点进行排序 + sort.SliceStable(parentNodeOut, func(i, j int) bool { + return parentNodeOut[i].OrderNum < parentNodeOut[j].OrderNum + }) out = deptTree(parentNodeOut, dept) return } @@ -63,6 +96,12 @@ func deptTree(parentNodeOut []*model.DeptOut, data []*model.DeptOut) (dataTree [ parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) } } + + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].OrderNum < v.Children[j].OrderNum + }) + deptTree(v.Children, data) } return parentNodeOut @@ -78,8 +117,9 @@ func (s *sSysDept) GetData(ctx context.Context, deptName string, status int) (da if deptName != "" { m = m.WhereLike(dao.SysDept.Columns().DeptName, "%"+deptName+"%") } + err = m.Where(dao.SysDept.Columns().IsDeleted, 0). - OrderDesc(dao.SysDept.Columns().OrderNum). + OrderAsc(dao.SysDept.Columns().OrderNum). Scan(&data) if err != nil { return @@ -98,28 +138,62 @@ func (s *sSysDept) Add(ctx context.Context, input *model.AddDeptInput) (err erro //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) dept = new(entity.SysDept) - if err := gconv.Scan(input, &dept); err != nil { - return err + if err = gconv.Scan(input, &dept); err != nil { + return + } + //判断是否有权限修改当前用户状态 + if input.ParentId != -1 { + var parentDept *entity.SysDept + parentDept, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentDept == nil { + return gerror.New("无权限选择当前部门") + } } + dept.IsDeleted = 0 dept.CreatedBy = uint(loginUserId) //开启事务管理 - err = dao.SysDept.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { - lastId, err1 := dao.SysDept.Ctx(ctx).Data(dept).InsertAndGetId() - if err1 != nil { - return err1 + err = dao.SysDept.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + result, err := dao.SysDept.Ctx(ctx).Data(do.SysDept{ + OrganizationId: dept.OrganizationId, + ParentId: dept.ParentId, + Ancestors: dept.Ancestors, + DeptName: dept.DeptName, + OrderNum: dept.OrderNum, + Leader: dept.Leader, + Phone: dept.Phone, + Email: dept.Email, + Status: dept.Status, + IsDeleted: dept.IsDeleted, + CreatedAt: dept.CreatedAt, + CreatedBy: dept.CreatedBy, + }).Insert() + if err != nil { + return } - err = setAncestors(ctx, input.ParentId, lastId) + //获取主键ID + lastInsertId, err := service.Sequences().GetSequences(ctx, result, dao.SysDept.Table(), dao.SysDept.Columns().DeptId) + if err != nil { + return + } + err = setAncestors(ctx, input.ParentId, lastInsertId) if err != nil { return err } - return err + return }) return } // Edit 修改部门 func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err error) { + if input.DeptId == input.ParentId { + return gerror.New("上级部门不能选择自己") + } + var dept1, dept2 *entity.SysDept //根据ID查看部门是否存在 dept1 = checkDeptId(ctx, input.DeptId, dept1) @@ -132,14 +206,26 @@ func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err er if dept2 != nil { return gerror.New("相同部门已存在,无法修改") } + //判断是否有权限修改当前用户状态 + if input.ParentId != -1 { + var parentDept *entity.SysDept + parentDept, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentDept == nil { + return gerror.New("无权限选择当前部门") + } + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - if err := gconv.Scan(input, &dept1); err != nil { - return err + if err = gconv.Scan(input, &dept1); err != nil { + return } dept1.UpdatedBy = loginUserId //开启事务管理 - err = dao.SysDept.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysDept.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { _, err = dao.SysDept.Ctx(ctx).Data(dept1). Where(dao.SysDept.Columns().DeptId, input.DeptId).Update() if err != nil { @@ -147,7 +233,7 @@ func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err er } //修改祖籍字段 if dept != input.ParentId { - err := setAncestors(ctx, input.ParentId, input.DeptId) + err = setAncestors(ctx, input.ParentId, input.DeptId) if err != nil { return gerror.New("祖籍修改失败") } @@ -159,7 +245,7 @@ func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err er for _, v := range value { newAncestors := strings.Replace(v.String(), deptAnces, lId, -1) //修改相关祖籍字段 - _, err := dao.SysDept.Ctx(ctx). + _, err = dao.SysDept.Ctx(ctx). Data(dao.SysDept.Columns().Ancestors, newAncestors). Where(dao.SysDept.Columns().Ancestors, v.String()).Update() if err != nil { @@ -175,7 +261,7 @@ func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err er newAncestors := strings.Replace(ancestors.String(), lId, "", -1) newAncestor := newAncestors + v.String() //修改相关祖籍字段 - _, err := dao.SysDept.Ctx(ctx). + _, err = dao.SysDept.Ctx(ctx). Data(dao.SysDept.Columns().Ancestors, newAncestor). Where(dao.SysDept.Columns().Ancestors, v.String()). WhereNot(dao.SysDept.Columns().DeptId, input.DeptId). @@ -193,7 +279,9 @@ func (s *sSysDept) Edit(ctx context.Context, input *model.EditDeptInput) (err er // Detail 部门详情 func (s *sSysDept) Detail(ctx context.Context, deptId int64) (entity *entity.SysDept, err error) { - _ = dao.SysDept.Ctx(ctx).Where(g.Map{ + m := dao.SysDept.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysDept.Columns().DeptId: deptId, }).Scan(&entity) return @@ -208,6 +296,7 @@ func (s *sSysDept) Del(ctx context.Context, deptId int64) (err error) { if dept == nil { return gerror.New("ID错误") } + //查询是否有子节点 num, err := dao.SysDept.Ctx(ctx).Where(g.Map{ dao.SysDept.Columns().ParentId: deptId, @@ -219,19 +308,17 @@ func (s *sSysDept) Del(ctx context.Context, deptId int64) (err error) { if num > 0 { return gerror.New("请先删除子节点!") } + loginUserId := service.Context().GetUserId(ctx) //更新部门信息 _, err = dao.SysDept.Ctx(ctx). Data(g.Map{ dao.SysDept.Columns().DeletedBy: uint(loginUserId), + dao.SysDept.Columns().DeletedAt: gtime.Now(), dao.SysDept.Columns().IsDeleted: 1, }). Where(dao.SysDept.Columns().DeptId, deptId). Update() - //删除部门信息 - _, err = dao.SysDept.Ctx(ctx). - Where(dao.SysDept.Columns().DeptId, deptId). - Delete() return } @@ -286,7 +373,9 @@ func checkDeptId(ctx context.Context, DeptId int64, dept *entity.SysDept) *entit // GetAll 获取全部部门数据 func (s *sSysDept) GetAll(ctx context.Context) (data []*entity.SysDept, err error) { - _ = dao.SysDept.Ctx(ctx).Where(g.Map{ + m := dao.SysDept.Ctx(ctx) + + _ = m.Where(g.Map{ dao.SysDept.Columns().Status: 1, dao.SysDept.Columns().IsDeleted: 0, }).Scan(&data) @@ -294,20 +383,21 @@ func (s *sSysDept) GetAll(ctx context.Context) (data []*entity.SysDept, err erro } func (s *sSysDept) GetFromCache(ctx context.Context) (list []*entity.SysDept, err error) { - err = g.Try(ctx, func(ctx context.Context) { - cache := common.Cache() - //从缓存获取 - iList := cache.GetOrSetFuncLock(ctx, consts.CacheSysDept, func(ctx context.Context) (value interface{}, err error) { - err = dao.SysDept.Ctx(ctx).Scan(&list) - liberr.ErrIsNil(ctx, err, "获取部门列表失败") - value = list + //从缓存获取 + iList, err := cache.Instance().GetOrSetFuncLock(ctx, consts.CacheSysDept, func(ctx context.Context) (value interface{}, err error) { + err = dao.SysDept.Ctx(ctx).Scan(&list) + if err != nil { return - }, 0, consts.CacheSysAuthTag) - if iList != nil { - err = gconv.Struct(iList, &list) - liberr.ErrIsNil(ctx, err) } - }) + value = list + return + }, 0) + if iList != nil { + err = gconv.Struct(iList.Val(), &list) + if err != nil { + return + } + } return } func (s *sSysDept) FindSonByParentId(deptList []*entity.SysDept, deptId int64) []*entity.SysDept { @@ -321,3 +411,28 @@ func (s *sSysDept) FindSonByParentId(deptList []*entity.SysDept, deptId int64) [ } return children } + +// FindDeptParentByChildrenId 根据子节点获取根节点 +func FindDeptParentByChildrenId(ctx context.Context, parentId int64) *entity.SysDept { + var dept *entity.SysDept + + _ = dao.SysDept.Ctx(ctx).Where(g.Map{ + dao.SysDept.Columns().DeptId: parentId, + }).Scan(&dept) + + if dept.ParentId != -1 { + return FindDeptParentByChildrenId(ctx, dept.ParentId) + } + return dept +} + +// GetDeptInfosByParentId 根据父ID获取子部门信息 +func (s *sSysDept) GetDeptInfosByParentId(ctx context.Context, parentId int) (data []*entity.SysDept, err error) { + m := dao.SysDept.Ctx(ctx) + _ = m.Where(g.Map{ + dao.SysDept.Columns().Status: 1, + dao.SysDept.Columns().IsDeleted: 0, + dao.SysDept.Columns().ParentId: parentId, + }).Scan(&data) + return +} diff --git a/internal/logic/system/sys_job.go b/internal/logic/system/sys_job.go index 846277b..98f135f 100644 --- a/internal/logic/system/sys_job.go +++ b/internal/logic/system/sys_job.go @@ -2,19 +2,21 @@ package system import ( "context" + "encoding/json" + "errors" "fmt" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/jobTask" - "strings" - "github.com/gogf/gf/v2/container/gset" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcron" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" + "sagooiot/internal/tasks" + "sagooiot/pkg/worker" ) type sSysJob struct { @@ -54,7 +56,7 @@ func (s *sSysJob) JobList(ctx context.Context, input *model.GetJobListInput) (to if input.PageSize == 0 { input.PageSize = consts.PageSize } - err = m.Page(input.PageNum, input.PageSize).Order("job_id asc").Scan(&out) + err = m.Page(input.PageNum, input.PageSize).Order("job_id desc").Scan(&out) if err != nil { err = gerror.New("获取数据失败") } @@ -68,8 +70,39 @@ func (s *sSysJob) GetJobs(ctx context.Context) (jobs []*model.SysJobOut, err err return } +// GetJobFuns 获取任务可用方法列表 +func (s *sSysJob) GetJobFuns(ctx context.Context) (jobsList []*model.SysJobFunListOut, err error) { + funList := worker.TasksInstance().GetTaskJobNameList() + for k, v := range funList { + var fun = new(model.SysJobFunListOut) + fun.FunName = k + fun.Explain = v + jobsList = append(jobsList, fun) + } + return +} + func (s *sSysJob) AddJob(ctx context.Context, input *model.SysJobAddInput) (err error) { - _, err = dao.SysJob.Ctx(ctx).Insert(input) + //获取task目录下是否绑定对应的方法 + checkName := worker.TasksInstance().CheckFuncName(input.InvokeTarget) + if !checkName { + errInfo := fmt.Sprintf("没有绑定对应的方法:%s", input.InvokeTarget) + return gerror.New(errInfo) + } + + _, err = dao.SysJob.Ctx(ctx).Data(do.SysJob{ + JobName: input.JobName, + JobParams: input.JobParams, + JobGroup: input.JobGroup, + InvokeTarget: input.InvokeTarget, + CronExpression: input.CronExpression, + MisfirePolicy: input.MisfirePolicy, + Concurrent: input.Concurrent, + Status: input.Status, + CreatedBy: input.CreateBy, + Remark: input.Remark, + CreatedAt: gtime.Now(), + }).Insert() return } @@ -89,58 +122,65 @@ func (s *sSysJob) GetJobInfoById(ctx context.Context, id int) (job *model.SysJob } func (s *sSysJob) EditJob(ctx context.Context, input *model.SysJobEditInput) error { - _, err := dao.SysJob.Ctx(ctx).FieldsEx(dao.SysJob.Columns().JobId, dao.SysJob.Columns().CreateBy).Where(dao.SysJob.Columns().JobId, input.JobId). + _, err := dao.SysJob.Ctx(ctx).FieldsEx(dao.SysJob.Columns().JobId, dao.SysJob.Columns().CreatedBy).Where(dao.SysJob.Columns().JobId, input.JobId). Update(input) - - // 同步定时任务到数据源和数据模型 - if input.JobGroup == "dataSourceJob" { - if input.InvokeTarget == "dataSource" { - err = service.DataSource().UpdateInterval(ctx, gconv.Uint64(input.JobParams), input.CronExpression) - } else if input.InvokeTarget == "dataTemplate" { - err = service.DataTemplate().UpdateInterval(ctx, gconv.Uint64(input.JobParams), input.CronExpression) - } - } return err } // JobStart 启动任务 func (s *sSysJob) JobStart(ctx context.Context, job *model.SysJobOut) error { //获取task目录下是否绑定对应的方法 - f := jobTask.TimeTaskList.GetByName(job.InvokeTarget) - if f == nil { - return gerror.New("没有绑定对应的方法") + checkName := worker.TasksInstance().CheckFuncName(job.InvokeTarget) + if !checkName { + errInfo := fmt.Sprintf("没有绑定对应的方法:%s", job.InvokeTarget) + return gerror.New(errInfo) } - //传参 - paramArr := strings.Split(job.JobParams, "|") - jobTask.TimeTaskList.EditParams(f.FuncName, paramArr) - jname := fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId) + //传参解析 + paramArr, err := worker.TasksInstance().ParseParameters(job.JobParams) + if err != nil { + g.Log().Error(ctx, err) + return err + } - rs := gcron.Search(jname) - if rs == nil { - newCtx := ctx - if job.JobGroup == "dataSourceJob" { - newCtx = s.WithValue(ctx, paramArr[0]) + taskData := tasks.TaskJob{ + ID: fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId), + TaskType: "Type-" + gconv.String(job.MisfirePolicy), + MethodName: job.InvokeTarget, + Params: paramArr, + Explain: job.JobName, + } + runPayload, _ := json.Marshal(taskData) + if job.MisfirePolicy == 1 { + err := worker.TasksInstance().Cron( + worker.WithRunCtx(context.Background()), + worker.WithRunUuid(taskData.ID), // 任务ID + worker.WithRunGroup(taskData.MethodName), // 任务组 + worker.WithRunExpr(job.CronExpression), + worker.WithRunTimeout(10), + worker.WithRunReplace(true), + worker.WithRunPayload(runPayload), + ) + if err != nil { + g.Log().Debug(ctx, taskData.MethodName, taskData.Explain, "启动任务失败") + return err } - if job.MisfirePolicy == 1 { - t, err := gcron.AddSingleton(newCtx, job.CronExpression, f.Run, jname) - if err != nil { - return err - } - if t == nil { - return gerror.New("启动任务失败") - } - } else { - t, err := gcron.AddOnce(newCtx, job.CronExpression, f.Run, jname) - if err != nil { - return err - } - if t == nil { - return gerror.New("启动任务失败") - } + } else { + err := worker.TasksInstance().Once( + worker.WithRunCtx(context.Background()), + worker.WithRunUuid(taskData.ID), // 任务ID + worker.WithRunGroup(taskData.MethodName), // 任务组 + worker.WithRunTimeout(10), + worker.WithRunNow(true), + worker.WithRunReplace(true), + worker.WithRunPayload(runPayload), + ) + if err != nil { + g.Log().Debug(ctx, taskData.MethodName, taskData.Explain, "启动任务失败") + return err } } - gcron.Start(jname) + if job.MisfirePolicy == 1 { job.Status = 0 _, err := dao.SysJob.Ctx(ctx).Where(dao.SysJob.Columns().JobId, job.JobId).Unscoped().Update(g.Map{ @@ -152,46 +192,62 @@ func (s *sSysJob) JobStart(ctx context.Context, job *model.SysJobOut) error { } // JobStartMult 批量启动任务 -func (s *sSysJob) JobStartMult(ctx context.Context, jobs []*model.SysJobOut) error { - +func (s *sSysJob) JobStartMult(ctx context.Context, jobsList []*model.SysJobOut) error { var jobIds = g.Slice{} - for _, job := range jobs { + for _, job := range jobsList { //获取task目录下是否绑定对应的方法 - f := jobTask.TimeTaskList.GetByName(job.InvokeTarget) - if f == nil { - return gerror.New("没有绑定对应的方法") + checkName := worker.TasksInstance().CheckFuncName(job.InvokeTarget) + if !checkName { + g.Log().Debugf(ctx, "没有绑定对应的方法:%s", job.InvokeTarget) + continue } - //传参 - paramArr := strings.Split(job.JobParams, "|") - jobTask.TimeTaskList.EditParams(f.FuncName, paramArr) - jname := fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId) + //传参解析 + paramArr, err := worker.TasksInstance().ParseParameters(job.JobParams) + if err != nil { + g.Log().Error(ctx, err) + continue + } + + taskData := tasks.TaskJob{ + ID: fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId), + TaskType: "Type-" + gconv.String(job.MisfirePolicy), + MethodName: job.InvokeTarget, + Params: paramArr, + Explain: job.JobName, + } + runPayload, _ := json.Marshal(taskData) - rs := gcron.Search(jname) - if rs == nil { - newCtx := ctx - if job.JobGroup == "dataSourceJob" { - newCtx = s.WithValue(ctx, paramArr[0]) + if job.MisfirePolicy == 1 { + err := worker.TasksInstance().Cron( + worker.WithRunCtx(ctx), + worker.WithRunUuid(taskData.ID), // 任务ID + worker.WithRunGroup(taskData.MethodName), // 任务组 + worker.WithRunExpr(job.CronExpression), + worker.WithRunTimeout(10), + worker.WithRunReplace(true), + worker.WithRunPayload(runPayload), + ) + if err != nil { + g.Log().Debug(ctx, taskData.MethodName, taskData.Explain, "启动任务失败") + continue } - if job.MisfirePolicy == 1 { - t, err := gcron.AddSingleton(newCtx, job.CronExpression, f.Run, jname) - if err != nil { - return err - } - if t == nil { - return gerror.New("启动任务失败") - } - } else { - t, err := gcron.AddOnce(newCtx, job.CronExpression, f.Run, jname) - if err != nil { - return err - } - if t == nil { - return gerror.New("启动任务失败") - } + } else { + err := worker.TasksInstance().Once( + worker.WithRunCtx(ctx), + worker.WithRunUuid(taskData.ID), // 任务ID + worker.WithRunGroup(taskData.MethodName), // 任务组 + worker.WithRunTimeout(10), + worker.WithRunNow(true), + worker.WithRunReplace(true), + worker.WithRunPayload(runPayload), + ) + if err != nil { + g.Log().Debug(ctx, taskData.MethodName, taskData.Explain, "启动任务失败") + continue } } - gcron.Start(jname) + if job.MisfirePolicy == 1 { jobIds = append(jobIds, job.JobId) } @@ -204,45 +260,62 @@ func (s *sSysJob) JobStartMult(ctx context.Context, jobs []*model.SysJobOut) err } // JobStop 停止任务 -func (s *sSysJob) JobStop(ctx context.Context, job *model.SysJobOut) error { +func (s *sSysJob) JobStop(ctx context.Context, job *model.SysJobOut) (err error) { //获取task目录下是否绑定对应的方法 - f := jobTask.TimeTaskList.GetByName(job.InvokeTarget) - if f == nil { - return gerror.New("没有绑定对应的方法") + checkName := worker.TasksInstance().CheckFuncName(job.InvokeTarget) + if !checkName { + errInfo := fmt.Sprintf("没有绑定对应的方法:%s", job.InvokeTarget) + return errors.New(errInfo) } - jname := fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId) - - rs := gcron.Search(jname) - if rs != nil { - gcron.Remove(jname) - } + taskJobId := fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId) + _ = worker.TasksInstance().Remove(ctx, taskJobId) job.Status = 1 - _, err := dao.SysJob.Ctx(ctx).Where(dao.SysJob.Columns().JobId, job.JobId).Unscoped().Update(g.Map{ + _, err = dao.SysJob.Ctx(ctx).Where(dao.SysJob.Columns().JobId, job.JobId).Unscoped().Update(g.Map{ dao.SysJob.Columns().Status: job.Status, }) - return err + return } // JobRun 执行任务 -func (s *sSysJob) JobRun(ctx context.Context, job *model.SysJobOut) error { +func (s *sSysJob) JobRun(ctx context.Context, job *model.SysJobOut) (err error) { //可以task目录下是否绑定对应的方法 - f := jobTask.TimeTaskList.GetByName(job.InvokeTarget) - if f == nil { - return gerror.New("当前task目录下没有绑定这个方法") + checkName := worker.TasksInstance().CheckFuncName(job.InvokeTarget) + if !checkName { + errInfo := fmt.Sprintf("没有绑定对应的方法:%s", job.InvokeTarget) + return errors.New(errInfo) } - //传参 - paramArr := strings.Split(job.JobParams, "|") - jobTask.TimeTaskList.EditParams(f.FuncName, paramArr) - newCtx := ctx - if job.JobGroup == "dataSourceJob" { - newCtx = s.WithValue(ctx, paramArr[0]) + + //传参解析 + paramArr, err := worker.TasksInstance().ParseParameters(job.JobParams) + if err != nil { + g.Log().Error(ctx, err) + return err } - task, err := gcron.AddOnce(newCtx, "@every 1s", f.Run) - if err != nil || task == nil { - return gerror.New("启动执行失败") + + taskData := tasks.TaskJob{ + ID: fmt.Sprintf("%s-job-%d", job.InvokeTarget, job.JobId), + TaskType: "Type-" + gconv.String(job.MisfirePolicy), + MethodName: job.InvokeTarget, + Params: paramArr, + Explain: job.JobName, } - return nil + runPayload, _ := json.Marshal(taskData) + + err = worker.TasksInstance().Once( + worker.WithRunCtx(context.Background()), + worker.WithRunUuid(taskData.ID), // 任务ID + worker.WithRunGroup(taskData.MethodName), // 任务组 + worker.WithRunTimeout(10), + worker.WithRunNow(true), + worker.WithRunReplace(true), + worker.WithRunPayload(runPayload), + ) + if err != nil { + errInfo := fmt.Sprintf(taskData.MethodName, taskData.Explain, "启动任务失败") + return errors.New(errInfo) + } + return } // DeleteJobByIds 删除任务 diff --git a/internal/logic/system/sys_login_log.go b/internal/logic/system/sys_login_log.go index f9e6515..aa56ee6 100644 --- a/internal/logic/system/sys_login_log.go +++ b/internal/logic/system/sys_login_log.go @@ -2,19 +2,22 @@ package system import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/do" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/utils" + "github.com/gogf/gf/v2/util/gconv" + "github.com/mssola/useragent" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/response" + "sagooiot/pkg/utility" + "sagooiot/pkg/utility/utils" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/grpool" "github.com/gogf/gf/v2/os/gtime" - "github.com/mssola/user_agent" ) type sSysLoginLog struct { @@ -42,7 +45,7 @@ func (s *sSysLoginLog) Invoke(ctx context.Context, data *model.LoginLogParams) { // Add 记录登录日志 func (s *sSysLoginLog) Add(ctx context.Context, params *model.LoginLogParams) { - ua := user_agent.New(params.UserAgent) + ua := useragent.New(params.UserAgent) browser, _ := ua.Browser() loginData := &do.SysLoginLog{ LoginName: params.Username, @@ -61,7 +64,7 @@ func (s *sSysLoginLog) Add(ctx context.Context, params *model.LoginLogParams) { } } -// GetList 获取访问日志数据列表 +// GetList 获取登录日志数据列表 func (s *sSysLoginLog) GetList(ctx context.Context, req *model.SysLoginLogInput) (total, page int, list []*model.SysLoginLogOut, err error) { m := dao.SysLoginLog.Ctx(ctx) if req.LoginName != "" { @@ -82,6 +85,10 @@ func (s *sSysLoginLog) GetList(ctx context.Context, req *model.SysLoginLogInput) if req.Status != -1 { m = m.Where(dao.SysLoginLog.Columns().Status, req.Status) } + if req.DateRange != nil && len(req.DateRange) > 0 { + m = m.WhereGTE(dao.SysLoginLog.Columns().LoginTime, req.DateRange[0]+" 00:00:00") + m = m.WhereLTE(dao.SysLoginLog.Columns().LoginTime, req.DateRange[1]+" 23:59:59") + } /*where,err := GetDataWhere(ctx, service.Context().GetUserId(ctx), new(entity.SysLoginLog)) if err != nil { return @@ -100,7 +107,7 @@ func (s *sSysLoginLog) GetList(ctx context.Context, req *model.SysLoginLogInput) req.PageNum = 1 } if req.PageSize == 0 { - req.PageSize = consts.DefaultPageSize + req.PageSize = consts.PageSize } //获取访问日志列表信息 err = m.Page(req.PageNum, req.PageSize).OrderDesc(dao.SysLoginLog.Columns().InfoId).Scan(&list) @@ -111,7 +118,7 @@ func (s *sSysLoginLog) GetList(ctx context.Context, req *model.SysLoginLogInput) return } -// Detail 访问日志详情 +// Detail 登录日志详情 func (s *sSysLoginLog) Detail(ctx context.Context, infoId int) (entity *entity.SysLoginLog, err error) { _ = dao.SysLoginLog.Ctx(ctx).Where(g.Map{ dao.SysLoginLog.Columns().InfoId: infoId, @@ -122,10 +129,10 @@ func (s *sSysLoginLog) Detail(ctx context.Context, infoId int) (entity *entity.S return } -// Del 根据ID删除访问日志 +// Del 根据ID删除登录日志 func (s *sSysLoginLog) Del(ctx context.Context, infoIds []int) (err error) { for _, infoId := range infoIds { - var SysLoginLog *entity.BaseDbLink + var SysLoginLog *entity.SysLoginLog _ = dao.SysLoginLog.Ctx(ctx).Where(g.Map{ dao.SysLoginLog.Columns().InfoId: infoId, }).Scan(&SysLoginLog) @@ -133,8 +140,62 @@ func (s *sSysLoginLog) Del(ctx context.Context, infoIds []int) (err error) { return gerror.New("ID错误") } } - //删除访问日志 + //删除登录日志 _, err = dao.SysLoginLog.Ctx(ctx).WhereIn(dao.SysLoginLog.Columns().InfoId, infoIds). Delete() return } + +// Export 导出登录日志列表 +func (s *sSysLoginLog) Export(ctx context.Context, req *model.SysLoginLogInput) (err error) { + m := dao.SysLoginLog.Ctx(ctx) + if req.LoginName != "" { + m = m.WhereLike(dao.SysLoginLog.Columns().LoginName, "%"+req.LoginName+"%") + } + if req.Ipaddr != "" { + m = m.WhereLike(dao.SysLoginLog.Columns().Ipaddr, "%"+req.Ipaddr+"%") + } + if req.LoginLocation != "" { + m = m.WhereLike(dao.SysLoginLog.Columns().LoginLocation, "%"+req.LoginLocation+"%") + } + if req.Browser != "" { + m = m.WhereLike(dao.SysLoginLog.Columns().Browser, "%"+req.Browser+"%") + } + if req.Os != "" { + m = m.WhereLike(dao.SysLoginLog.Columns().Os, "%"+req.Os+"%") + } + if req.Status != -1 { + m = m.Where(dao.SysLoginLog.Columns().Status, req.Status) + } + if req.DateRange != nil && len(req.DateRange) > 0 { + m = m.WhereGTE(dao.SysLoginLog.Columns().LoginTime, req.DateRange[0]+" 00:00:00") + m = m.WhereLTE(dao.SysLoginLog.Columns().LoginTime, req.DateRange[1]+" 23:59:59") + } + //获取访问日志列表信息 + var outList []*model.SysLoginLogOut + err = m.OrderDesc(dao.SysLoginLog.Columns().InfoId).Scan(&outList) + if err != nil { + err = gerror.New("获取访问日志列表失败") + return + } + + //处理数据并导出 + var resData []interface{} + for _, out := range outList { + var exportOut = new(model.SysLoginLogExportOut) + if err = gconv.Scan(out, exportOut); err != nil { + return + } + if out.Status == 1 { + exportOut.Status = "成功" + } else if out.Status == 0 { + exportOut.Status = "失败" + } + resData = append(resData, exportOut) + } + data := utility.ToExcel(resData) + var request = g.RequestFromCtx(ctx) + response.ToXls(request, data, "SysLoginLog") + + return +} diff --git a/internal/logic/system/sys_menu.go b/internal/logic/system/sys_menu.go index 682aa48..c05ca44 100644 --- a/internal/logic/system/sys_menu.go +++ b/internal/logic/system/sys_menu.go @@ -6,13 +6,16 @@ import ( "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sort" ) type sSysMenu struct { @@ -28,7 +31,6 @@ func init() { // GetAll 获取全部菜单数据 func (s *sSysMenu) GetAll(ctx context.Context) (data []*entity.SysMenu, err error) { - cache := common.Cache() err = dao.SysMenu.Ctx(ctx).Where(g.Map{ dao.SysMenu.Columns().Status: 1, dao.SysMenu.Columns().IsDeleted: 0, @@ -37,9 +39,12 @@ func (s *sSysMenu) GetAll(ctx context.Context) (data []*entity.SysMenu, err erro return } if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysMenu, data, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenu, data, 0) + if err != nil { + return + } } else { - cache.Remove(ctx, consts.CacheSysMenu) + _, err = cache.Instance().Remove(ctx, consts.CacheSysMenu) } return } @@ -56,15 +61,64 @@ func (s *sSysMenu) GetTree(ctx context.Context, title string, status int) (data if err = gconv.Scan(v, &parentNode); err != nil { return } - parentNodeOut = append(parentNodeOut, parentNode) + + var isExist = false + for _, menuOut := range parentNodeOut { + if menuOut.Id == parentNode.Id { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } + } else { + //查找根节点 + var parentMenu *entity.SysMenu + parentMenu, err = FindMenuParentByChildrenId(ctx, int(v.ParentId)) + if err != nil { + + } + if err = gconv.Scan(parentMenu, &parentNode); err != nil { + return + } + var isExist = false + for _, menuOut := range parentNodeOut { + if menuOut.Id == int64(parentMenu.Id) { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } } } + //对父节点进行排序 + sort.SliceStable(parentNodeOut, func(i, j int) bool { + return parentNodeOut[i].Weigh > parentNodeOut[j].Weigh + }) data = MenuTree(parentNodeOut, menuInfo) } return } +// FindMenuParentByChildrenId 根据子节点获取根节点 +func FindMenuParentByChildrenId(ctx context.Context, parentId int) (out *entity.SysMenu, err error) { + err = dao.SysMenu.Ctx(ctx).Where(g.Map{ + dao.SysMenu.Columns().Id: parentId, + dao.SysMenu.Columns().IsDeleted: 0, + }).Scan(&out) + if err != nil { + return + } + if out.ParentId != -1 { + return FindMenuParentByChildrenId(ctx, out.ParentId) + } + return +} + // MenuTree 生成树结构 func MenuTree(parentNodeOut []*model.SysMenuOut, data []*model.SysMenuOut) (dataTree []*model.SysMenuOut) { //循环所有一级菜单 @@ -79,6 +133,10 @@ func MenuTree(parentNodeOut []*model.SysMenuOut, data []*model.SysMenuOut) (data parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) } } + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].Weigh > v.Children[j].Weigh + }) MenuTree(v.Children, data) } return parentNodeOut @@ -100,7 +158,31 @@ func (s *sSysMenu) Add(ctx context.Context, input *model.AddMenuInput) (err erro } menu.IsDeleted = 0 menu.CreatedBy = uint(loginUserId) - _, err = dao.SysMenu.Ctx(ctx).Data(menu).Insert() + _, err = dao.SysMenu.Ctx(ctx).Data(do.SysMenu{ + ParentId: menu.ParentId, + Name: menu.Name, + Title: menu.Title, + Icon: menu.Icon, + Condition: menu.Condition, + Remark: menu.Remark, + MenuType: menu.MenuType, + Weigh: menu.Weigh, + IsHide: menu.IsHide, + Path: menu.Path, + Component: menu.Component, + IsLink: menu.IsLink, + ModuleType: menu.ModuleType, + ModelId: menu.ModelId, + IsIframe: menu.IsIframe, + IsCached: menu.IsCached, + Redirect: menu.Redirect, + IsAffix: menu.IsAffix, + LinkUrl: menu.LinkUrl, + Status: menu.Status, + IsDeleted: menu.IsDeleted, + CreatedBy: menu.CreatedBy, + CreatedAt: menu.CreatedAt, + }).Insert() if err != nil { return err } @@ -184,10 +266,9 @@ func (s *sSysMenu) Del(ctx context.Context, menuId int64) (err error) { loginUserId := service.Context().GetUserId(ctx) _, err = dao.SysMenu.Ctx(ctx).Data(g.Map{ dao.SysMenu.Columns().DeletedBy: uint(loginUserId), + dao.SysMenu.Columns().DeletedAt: gtime.Now(), dao.SysMenu.Columns().IsDeleted: 1, }).Where(dao.SysMenu.Columns().Id, menuId).Update() - //删除菜单信息 - _, err = dao.SysMenu.Ctx(ctx).Where(dao.SysMenu.Columns().Id, menuId).Delete() //获取所有的菜单 _, err = s.GetAll(ctx) if err != nil { @@ -221,7 +302,7 @@ func checkMenuJoin(ctx context.Context, menuId int64) string { dao.SysMenuButton.Columns().IsDeleted: 0, }).Count() if num > 0 { - return "存在菜单列表关联!" + return "存在菜单按钮关联!" } //查询关联按钮 num, _ = dao.SysMenuColumn.Ctx(ctx).Where(g.Map{ @@ -229,7 +310,7 @@ func checkMenuJoin(ctx context.Context, menuId int64) string { dao.SysMenuColumn.Columns().IsDeleted: 0, }).Count() if num > 0 { - return "存在菜单按钮关联!" + return "存在菜单列表关联!" } return "" } @@ -264,19 +345,20 @@ func checkMenuId(ctx context.Context, MenuId int64, menu *entity.SysMenu) *entit // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 func (s *sSysMenu) GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenu, err error) { - cache := common.Cache() var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenu) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenu) if err != nil { return } var tmpMenuInfo []*entity.SysMenu - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpMenuInfo) var menuInfo []*entity.SysMenu //根据菜单ID数组获取菜单列表信息 - if tmpData != nil { + if tmpData.Val() != nil { + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpMenuInfo); err != nil { + return + } for _, menuId := range menuIds { for _, menuTmp := range tmpMenuInfo { if menuId == int(menuTmp.Id) { diff --git a/internal/logic/system/sys_menu_api.go b/internal/logic/system/sys_menu_api.go index 0de8f09..699fa68 100644 --- a/internal/logic/system/sys_menu_api.go +++ b/internal/logic/system/sys_menu_api.go @@ -6,11 +6,12 @@ import ( "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sSysMenuApi struct { @@ -24,18 +25,47 @@ func init() { service.RegisterSysMenuApi(sysMenuApiNew()) } +// MenuApiList 根据菜单ID获取API列表 +func (s *sSysMenuApi) MenuApiList(ctx context.Context, menuId int) (out []*model.SysApiAllOut, err error) { + //获取所有的菜单 + menuApiInfo, err := s.GetInfoByMenuId(ctx, menuId) + if err != nil { + return + } + if menuApiInfo != nil && len(menuApiInfo) > 0 { + var apiIds []int + for _, menuApi := range menuApiInfo { + apiIds = append(apiIds, menuApi.ApiId) + } + if apiIds != nil && len(apiIds) > 0 { + var apiInfos []*entity.SysApi + apiInfos, err = service.SysApi().GetInfoByIds(ctx, apiIds) + if err != nil { + return + } + if apiInfos != nil { + if err = gconv.Scan(apiInfos, &out); err != nil { + return + } + } + } + } + return +} + // GetInfoByIds 根据IDS数组获取菜单信息 func (s *sSysMenuApi) GetInfoByIds(ctx context.Context, ids []int) (data []*entity.SysMenuApi, err error) { - cache := common.Cache() //获取缓存信息 var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuApi) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuApi) if err != nil { return } var tmpSysMenuApi []*entity.SysMenuApi if tmpData.Val() != nil { - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuApi) + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuApi); err != nil { + return + } for _, v := range ids { for _, tmp := range tmpSysMenuApi { if v == int(tmp.Id) { @@ -54,17 +84,16 @@ func (s *sSysMenuApi) GetInfoByIds(ctx context.Context, ids []int) (data []*enti // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 func (s *sSysMenuApi) GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuApi, err error) { - cache := common.Cache() //获取缓存信息 for _, v := range menuIds { var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuApi+"_"+gconv.String(v)) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuApi+"_"+gconv.String(v)) if err != nil { return } if tmpData.Val() != nil { var sysMenuApi []*entity.SysMenuApi - json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuApi) + err = json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuApi) data = append(data, sysMenuApi...) } } @@ -84,3 +113,34 @@ func (s *sSysMenuApi) GetInfoByApiId(ctx context.Context, apiId int) (data []*en }).Scan(&data) return } + +// GetAll 获取所有信息 +func (s *sSysMenuApi) GetAll(ctx context.Context) (data []*entity.SysMenuApi, err error) { + err = dao.SysMenuApi.Ctx(ctx).Where(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 0, + }).Scan(&data) + return +} + +// GetInfoByMenuId 根据菜单ID获取菜单信息 +func (s *sSysMenuApi) GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuApi, err error) { + err = dao.SysMenuApi.Ctx(ctx).Where(g.Map{ + dao.SysMenuApi.Columns().IsDeleted: 0, + dao.SysMenuApi.Columns().MenuId: menuId, + }).Scan(&data) + return +} + +// FindParentByChildrenId 根据子节点获取根节点 +func FindParentByChildrenId(ctx context.Context, parentId int) *entity.SysApi { + var api *entity.SysApi + + _ = dao.SysApi.Ctx(ctx).Where(g.Map{ + dao.SysApi.Columns().Id: parentId, + }).Scan(&api) + + if api.ParentId != -1 { + return FindParentByChildrenId(ctx, api.ParentId) + } + return api +} diff --git a/internal/logic/system/sys_menu_button.go b/internal/logic/system/sys_menu_button.go index 8abdd63..6b4d6fd 100644 --- a/internal/logic/system/sys_menu_button.go +++ b/internal/logic/system/sys_menu_button.go @@ -4,12 +4,14 @@ import ( "context" "encoding/json" "github.com/gogf/gf/v2/container/gvar" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -28,14 +30,48 @@ func init() { } // GetList 获取全部菜单按钮数据 -func (s *sSysMenuButton) GetList(ctx context.Context, status int, name string, menuId int) (data []model.UserMenuButtonRes, err error) { - var menuButton []model.UserMenuButtonRes - menuButton, err = s.GetData(ctx, status, name, menuId, menuButton) - return menuButton, err +func (s *sSysMenuButton) GetList(ctx context.Context, status int, name string, menuId int) (data []*model.UserMenuButtonOut, err error) { + var menuButton []model.UserMenuButtonOut + menuButton, err = s.GetData(ctx, status, name, menuId) + + var parentNodeOut []*model.UserMenuButtonOut + if menuButton != nil { + //获取所有的根节点 + for _, v := range menuButton { + var parentNode *model.UserMenuButtonOut + if v.ParentId == -1 { + if err = gconv.Scan(v, &parentNode); err != nil { + return + } + parentNodeOut = append(parentNodeOut, parentNode) + } + } + } + data = ButtonTree(parentNodeOut, menuButton) + return +} + +// ButtonTree MenuButtonTree 生成菜单按钮树结构 +func ButtonTree(parentNodeOut []*model.UserMenuButtonOut, data []model.UserMenuButtonOut) (dataTree []*model.UserMenuButtonOut) { + //循环所有一级菜单 + for k, v := range parentNodeOut { + //查询所有该菜单下的所有子菜单 + for _, j := range data { + var node *model.UserMenuButtonOut + if j.ParentId == v.Id { + if err := gconv.Scan(j, &node); err != nil { + return + } + parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) + } + } + ButtonTree(v.Children, data) + } + return parentNodeOut } // GetData 执行获取数据操作 -func (s *sSysMenuButton) GetData(ctx context.Context, status int, name string, menuId int, menuButton []model.UserMenuButtonRes) (data []model.UserMenuButtonRes, err error) { +func (s *sSysMenuButton) GetData(ctx context.Context, status int, name string, menuId int) (data []model.UserMenuButtonOut, err error) { m := dao.SysMenuButton.Ctx(ctx) if status != -1 { @@ -48,8 +84,8 @@ func (s *sSysMenuButton) GetData(ctx context.Context, status int, name string, m err = m.Where(g.Map{ dao.SysMenuButton.Columns().IsDeleted: 0, dao.SysMenuButton.Columns().MenuId: menuId, - }).Scan(&menuButton) - return menuButton, err + }).Scan(&data) + return } // Add 添加菜单按钮 @@ -72,7 +108,17 @@ func (s *sSysMenuButton) Add(ctx context.Context, input *model.AddMenuButtonInpu } menuButton.IsDeleted = 0 menuButton.CreatedBy = uint(loginUserId) - _, err = dao.SysMenuButton.Ctx(ctx).Data(menuButton).Insert() + _, err = dao.SysMenuButton.Ctx(ctx).Data(do.SysMenuButton{ + ParentId: menuButton.ParentId, + MenuId: menuButton.MenuId, + Name: menuButton.Name, + Types: menuButton.Types, + Description: menuButton.Description, + Status: menuButton.Status, + IsDeleted: menuButton.IsDeleted, + CreatedBy: menuButton.CreatedBy, + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return err } @@ -165,12 +211,11 @@ func (s *sSysMenuButton) Del(ctx context.Context, id int64) (err error) { _, err = dao.SysMenuButton.Ctx(ctx). Data(g.Map{ dao.SysMenuButton.Columns().DeletedBy: uint(loginUserId), + dao.SysMenuButton.Columns().DeletedAt: gtime.Now(), dao.SysMenuButton.Columns().IsDeleted: 1, }).Where(dao.SysMenuButton.Columns().Id, id). Update() - //删除菜单按钮信息 - _, err = dao.SysMenuButton.Ctx(ctx).Where(dao.SysMenuButton.Columns().Id, id). - Delete() + //获取该菜单下所有的菜单按钮 _, err = s.GetInfoByMenuId(ctx, menuButton.MenuId) if err != nil { @@ -186,16 +231,17 @@ func (s *sSysMenuButton) Del(ctx context.Context, id int64) (err error) { // GetInfoByButtonIds 根据按钮ID数组获取菜单按钮信息 func (s *sSysMenuButton) GetInfoByButtonIds(ctx context.Context, ids []int) (data []*entity.SysMenuButton, err error) { - cache := common.Cache() var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuButton) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuButton) var tmpSysMenuButton []*entity.SysMenuButton var menuButtonInfo []*entity.SysMenuButton //根据菜单ID数组获取菜单列表信息 if tmpData.Val() != nil { - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuButton) + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuButton); err != nil { + return + } for _, id := range ids { for _, tmp := range tmpSysMenuButton { if id == int(tmp.Id) { @@ -219,14 +265,13 @@ func (s *sSysMenuButton) GetInfoByButtonIds(ctx context.Context, ids []int) (dat // GetInfoByMenuIds 根据菜单ID数组获取菜单按钮信息 func (s *sSysMenuButton) GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuButton, err error) { - cache := common.Cache() //获取缓存菜单按钮信息 for _, v := range menuIds { var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuButton+"_"+gconv.String(v)) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuButton+"_"+gconv.String(v)) if tmpData.Val() != nil { var sysMenuButton []*entity.SysMenuButton - json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuButton) + err = json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuButton) data = append(data, sysMenuButton...) } } @@ -241,7 +286,6 @@ func (s *sSysMenuButton) GetInfoByMenuIds(ctx context.Context, menuIds []int) (d // GetInfoByMenuId 根据菜单ID数组获取菜单按钮信息 func (s *sSysMenuButton) GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuButton, err error) { - cache := common.Cache() err = dao.SysMenuButton.Ctx(ctx).Where(g.Map{ dao.SysMenuButton.Columns().IsDeleted: 0, dao.SysMenuButton.Columns().Status: 1, @@ -251,9 +295,9 @@ func (s *sSysMenuButton) GetInfoByMenuId(ctx context.Context, menuId int) (data return } if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId), data, 0) + _ = cache.Instance().Set(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId), data, 0) } else { - cache.Remove(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId)) + _, err = cache.Instance().Remove(ctx, consts.CacheSysMenuButton+"_"+gconv.String(menuId)) } return @@ -261,7 +305,6 @@ func (s *sSysMenuButton) GetInfoByMenuId(ctx context.Context, menuId int) (data // GetAll 获取所有的按钮信息 func (s *sSysMenuButton) GetAll(ctx context.Context) (data []*entity.SysMenuButton, err error) { - cache := common.Cache() err = dao.SysMenuButton.Ctx(ctx).Where(g.Map{ dao.SysMenuButton.Columns().IsDeleted: 0, dao.SysMenuButton.Columns().Status: 1, @@ -271,9 +314,9 @@ func (s *sSysMenuButton) GetAll(ctx context.Context) (data []*entity.SysMenuButt return } if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysMenuButton, data, 0) + _ = cache.Instance().Set(ctx, consts.CacheSysMenuButton, data, 0) } else { - cache.Remove(ctx, consts.CacheSysMenuButton) + _, err = cache.Instance().Remove(ctx, consts.CacheSysMenuButton) } return diff --git a/internal/logic/system/sys_menu_column.go b/internal/logic/system/sys_menu_column.go index e5543d5..0f8ac8f 100644 --- a/internal/logic/system/sys_menu_column.go +++ b/internal/logic/system/sys_menu_column.go @@ -4,12 +4,14 @@ import ( "context" "encoding/json" "github.com/gogf/gf/v2/container/gvar" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -28,14 +30,49 @@ func init() { } // GetList 获取全部菜单列表数据 -func (s *sSysMenuColumn) GetList(ctx context.Context, input *model.MenuColumnDoInput) (data []model.UserMenuColumnRes, err error) { - var menuColumn []model.UserMenuColumnRes - menuColumn, err = s.GetData(ctx, input, menuColumn) - return menuColumn, err +func (s *sSysMenuColumn) GetList(ctx context.Context, input *model.MenuColumnDoInput) (data []*model.UserMenuColumnOut, err error) { + menuColumnOut, err := s.GetData(ctx, input) + if err != nil { + return + } + var parentNodeOut []*model.UserMenuColumnOut + if menuColumnOut != nil { + //获取所有的根节点 + for _, v := range menuColumnOut { + var parentNode *model.UserMenuColumnOut + if v.ParentId == -1 { + if err = gconv.Scan(v, &parentNode); err != nil { + return + } + parentNodeOut = append(parentNodeOut, parentNode) + } + } + data = ColumnTree(parentNodeOut, menuColumnOut) + } + return +} + +// ColumnTree MenuColumnTree 生成菜单列表树结构 +func ColumnTree(parentNodeOut []*model.UserMenuColumnOut, data []model.UserMenuColumnOut) (dataTree []*model.UserMenuColumnOut) { + //循环所有一级菜单 + for k, v := range parentNodeOut { + //查询所有该菜单下的所有子菜单 + for _, j := range data { + var node *model.UserMenuColumnOut + if j.ParentId == v.Id { + if err := gconv.Scan(j, &node); err != nil { + return + } + parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) + } + } + ColumnTree(v.Children, data) + } + return parentNodeOut } // GetData 执行获取数据操作 -func (s *sSysMenuColumn) GetData(ctx context.Context, input *model.MenuColumnDoInput, menuColumn []model.UserMenuColumnRes) (data []model.UserMenuColumnRes, err error) { +func (s *sSysMenuColumn) GetData(ctx context.Context, input *model.MenuColumnDoInput) (data []model.UserMenuColumnOut, err error) { m := dao.SysMenuColumn.Ctx(ctx) //模糊查询菜单列表名称 if input.Name != "" { @@ -56,9 +93,8 @@ func (s *sSysMenuColumn) GetData(ctx context.Context, input *model.MenuColumnDoI err = m.Where(g.Map{ dao.SysMenuColumn.Columns().MenuId: input.MenuId, dao.SysMenuColumn.Columns().IsDeleted: 0, - }). - Scan(&menuColumn) - return menuColumn, err + }).Scan(&data) + return } // Add 添加菜单列表 @@ -82,7 +118,17 @@ func (s *sSysMenuColumn) Add(ctx context.Context, input *model.AddMenuColumnInpu } menuColumn.IsDeleted = 0 menuColumn.CreatedBy = uint(loginUserId) - _, err = dao.SysMenuColumn.Ctx(ctx).Data(menuColumn).Insert() + _, err = dao.SysMenuColumn.Ctx(ctx).Data(do.SysMenuColumn{ + ParentId: menuColumn.ParentId, + MenuId: menuColumn.MenuId, + Name: menuColumn.Name, + Code: menuColumn.Code, + Description: menuColumn.Description, + Status: menuColumn.Status, + IsDeleted: menuColumn.IsDeleted, + CreatedBy: menuColumn.CreatedBy, + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return err } @@ -177,11 +223,10 @@ func (s *sSysMenuColumn) Del(ctx context.Context, Id int64) (err error) { _, err = dao.SysMenuColumn.Ctx(ctx). Data(g.Map{ dao.SysMenuColumn.Columns().DeletedBy: uint(loginUserId), + dao.SysMenuColumn.Columns().DeletedAt: gtime.Now(), dao.SysMenuColumn.Columns().IsDeleted: 1, }).Where(dao.SysMenuColumn.Columns().Id, Id). Update() - //删除菜单列表信息 - _, err = dao.SysMenuColumn.Ctx(ctx).Where(dao.SysMenuColumn.Columns().Id, Id).Delete() //获取该菜单下所有的菜单按钮 _, err = s.GetInfoByMenuId(ctx, menuColumn.MenuId) @@ -267,9 +312,8 @@ func checkMenuColumnCode(ctx context.Context, menu int, code string, menuColumn // GetInfoByColumnIds 根据列表ID数组获取菜单信息 func (s *sSysMenuColumn) GetInfoByColumnIds(ctx context.Context, ids []int) (data []*entity.SysMenuColumn, err error) { - cache := common.Cache() var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuColumn) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuColumn) if err != nil { return } @@ -279,7 +323,9 @@ func (s *sSysMenuColumn) GetInfoByColumnIds(ctx context.Context, ids []int) (dat var menuColumnInfo []*entity.SysMenuColumn //根据菜单ID数组获取菜单列表信息 if tmpData.Val() != nil { - json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuColumn) + if err = json.Unmarshal([]byte(tmpData.Val().(string)), &tmpSysMenuColumn); err != nil { + return + } for _, id := range ids { for _, tmp := range tmpSysMenuColumn { if id == int(tmp.Id) { @@ -303,17 +349,16 @@ func (s *sSysMenuColumn) GetInfoByColumnIds(ctx context.Context, ids []int) (dat // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 func (s *sSysMenuColumn) GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuColumn, err error) { - cache := common.Cache() //获取缓存菜单按钮信息 for _, v := range menuIds { var tmpData *gvar.Var - tmpData = cache.Get(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(v)) + tmpData, err = cache.Instance().Get(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(v)) if err != nil { return } if tmpData.Val() != nil { var sysMenuColumn []*entity.SysMenuColumn - json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuColumn) + err = json.Unmarshal([]byte(tmpData.Val().(string)), &sysMenuColumn) data = append(data, sysMenuColumn...) } } @@ -328,7 +373,6 @@ func (s *sSysMenuColumn) GetInfoByMenuIds(ctx context.Context, menuIds []int) (d // GetInfoByMenuId 根据菜单ID获取菜单信息 func (s *sSysMenuColumn) GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuColumn, err error) { - cache := common.Cache() err = dao.SysMenuColumn.Ctx(ctx).Where(g.Map{ dao.SysMenuColumn.Columns().IsDeleted: 0, dao.SysMenuColumn.Columns().Status: 1, @@ -338,16 +382,18 @@ func (s *sSysMenuColumn) GetInfoByMenuId(ctx context.Context, menuId int) (data return } if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId), data, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId), data, 0) + if err != nil { + return nil, err + } } else { - cache.Remove(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId)) + _, err = cache.Instance().Remove(ctx, consts.CacheSysMenuColumn+"_"+gconv.String(menuId)) } return } // GetAll 获取所有的列表信息 func (s *sSysMenuColumn) GetAll(ctx context.Context) (data []*entity.SysMenuColumn, err error) { - cache := common.Cache() err = dao.SysMenuColumn.Ctx(ctx).Where(g.Map{ dao.SysMenuColumn.Columns().IsDeleted: 0, dao.SysMenuColumn.Columns().Status: 1, @@ -356,9 +402,12 @@ func (s *sSysMenuColumn) GetAll(ctx context.Context) (data []*entity.SysMenuColu return } if data != nil && len(data) > 0 { - cache.Set(ctx, consts.CacheSysMenuColumn, data, 0) + err = cache.Instance().Set(ctx, consts.CacheSysMenuColumn, data, 0) + if err != nil { + return nil, err + } } else { - cache.Remove(ctx, consts.CacheSysMenuColumn) + _, err = cache.Instance().Remove(ctx, consts.CacheSysMenuColumn) } return } diff --git a/internal/logic/system/sys_message.go b/internal/logic/system/sys_message.go new file mode 100644 index 0000000..c8a0a4f --- /dev/null +++ b/internal/logic/system/sys_message.go @@ -0,0 +1,390 @@ +package system + +import ( + "context" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "strings" +) + +type sSysMessage struct { +} + +func sysMessageNew() *sSysMessage { + return &sSysMessage{} +} + +func init() { + service.RegisterSysMessage(sysMessageNew()) +} + +// GetList 获取列表数据 +func (s *sSysMessage) GetList(ctx context.Context, input *model.MessageListDoInput) (total int, out []*model.MessageListOut, err error) { + m := g.Model(dao.SysMessagereceive.Table() + " l") + m = m.LeftJoin(dao.SysMessage.Table()+" s", "s."+dao.SysMessage.Columns().Id+"=l."+dao.SysMessagereceive.Columns().MessageId) + if input.Title != "" { + m = m.WhereLike("s."+dao.SysMessage.Columns().Title, "%"+input.Title+"%") + } + if input.Types != -1 { + m = m.Where("s."+dao.SysMessage.Columns().Types, input.Types) + } + //获取当前用户信息 + loginUserId := service.Context().GetUserId(ctx) + m = m.Where("l."+dao.SysMessagereceive.Columns().UserId, loginUserId) + + m = m.Where("l."+dao.SysMessagereceive.Columns().IsDeleted, 0) + + //获取总数 + total, err = m.Count() + if err != nil { + err = gerror.New("获取消息列表数据失败") + return + } + if input.PageNum == 0 { + input.PageNum = 1 + } + if input.PageSize == 0 { + input.PageSize = consts.PageSize + } + err = m.Page(input.PageNum, input.PageSize).Fields("l.*").WithAll().OrderDesc(dao.SysMessage.Columns().CreatedAt).Scan(&out) + + if err != nil { + err = gerror.New("获取消息列表失败") + return + } + return +} + +// Add 新增 +func (s *sSysMessage) Add(ctx context.Context, messageInfo *model.AddMessageInput) (err error) { + if messageInfo.Title == "" { + err = gerror.New("标题不能为空") + return + } + if messageInfo.Content == "" { + err = gerror.New("内容不能为空") + return + } + if messageInfo.Scope == 0 { + err = gerror.New("消息范围不能为空") + return + } + if messageInfo.Types == 0 { + err = gerror.New("消息类型不能为空") + return + } + err = dao.SysMessage.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + //新增消息 + /*var message = new(entity.SysMessage) + message.Types = messageInfo.Types + message.Title = messageInfo.Title + message.Scope = messageInfo.Scope + message.Content = messageInfo.Content*/ + /*message.IsDeleted = 0*/ + loginUserId := service.Context().GetUserId(ctx) + /*message.CreatedBy = uint(loginUserId)*/ + result, err := dao.SysMessage.Ctx(ctx).Data(do.SysMessage{ + Title: messageInfo.Title, + Types: messageInfo.Types, + Scope: messageInfo.Scope, + Content: messageInfo.Content, + IsDeleted: 0, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() + if err != nil { + return + } + + //获取主键ID + lastInsertId, err := service.Sequences().GetSequences(ctx, result, dao.SysMessage.Table(), dao.SysMessage.Columns().Id) + if err != nil { + return + } + + //判断消息范围 + var sysDictData *entity.SysDictData + err = dao.SysDictData.Ctx(ctx).Where(dao.SysDictData.Columns().DictCode, messageInfo.Scope).Scan(&sysDictData) + if sysDictData == nil { + err = gerror.New("类型错误") + return + } + var messagereceives []*entity.SysMessagereceive + if strings.EqualFold(sysDictData.DictValue, "1") { + //系统消息 + //查询所有用户 + var user []*entity.SysUser + err = dao.SysUser.Ctx(ctx).Where(g.Map{ + dao.SysUser.Columns().IsDeleted: 0, + dao.SysUser.Columns().Status: 1, + }).Scan(&user) + if user != nil && len(user) > 0 { + for _, v := range user { + var messagereceive = new(entity.SysMessagereceive) + messagereceive.MessageId = int(lastInsertId) + messagereceive.UserId = int(v.Id) + messagereceive.IsRead = 0 + messagereceive.IsPush = 0 + messagereceive.IsDeleted = 0 + messagereceives = append(messagereceives, messagereceive) + } + } + } else if strings.EqualFold(sysDictData.DictValue, "2") { + if messageInfo.ObjectId == 0 { + err = gerror.New("推送组织不能为空") + return + } + //组织消息 + var dept []*entity.SysDept + err = dao.SysDept.Ctx(ctx).Where(g.Map{ + dao.SysDept.Columns().OrganizationId: messageInfo.ObjectId, + dao.SysDept.Columns().IsDeleted: 0, + dao.SysDept.Columns().Status: 1, + }).Scan(&dept) + var deptId []int64 + if dept != nil && len(dept) > 0 { + for _, v := range dept { + deptId = append(deptId, v.DeptId) + } + } + //根据部门ID获取用户信息 + var user []*entity.SysUser + err = dao.SysUser.Ctx(ctx).Where(g.Map{ + dao.SysUser.Columns().IsDeleted: 0, + dao.SysUser.Columns().Status: 1, + }).WhereIn(dao.SysUser.Columns().DeptId, deptId).Scan(&user) + + if user != nil && len(user) > 0 { + for _, v := range user { + var messagereceive = new(entity.SysMessagereceive) + messagereceive.MessageId = int(lastInsertId) + messagereceive.UserId = int(v.Id) + messagereceive.IsRead = 0 + messagereceive.IsPush = 0 + messagereceive.IsDeleted = 0 + messagereceives = append(messagereceives, messagereceive) + } + } + + } else if strings.EqualFold(sysDictData.DictValue, "3") { + if messageInfo.ObjectId == 0 { + err = gerror.New("推送部门不能为空") + return + } + //部门消息 + //根据部门ID获取用户信息 + var user []*entity.SysUser + err = dao.SysUser.Ctx(ctx).Where(g.Map{ + dao.SysUser.Columns().IsDeleted: 0, + dao.SysUser.Columns().Status: 1, + dao.SysUser.Columns().DeptId: messageInfo.ObjectId, + }).Scan(&user) + if user != nil && len(user) > 0 { + for _, v := range user { + var messagereceive = new(entity.SysMessagereceive) + messagereceive.MessageId = int(lastInsertId) + messagereceive.UserId = int(v.Id) + messagereceive.IsRead = 0 + messagereceive.IsPush = 0 + messagereceive.IsDeleted = 0 + messagereceives = append(messagereceives, messagereceive) + } + } + } else if strings.EqualFold(sysDictData.DictValue, "4") { + if messageInfo.ObjectId == 0 { + err = gerror.New("推送用户不能为空") + return + } + //用户消息 + num, _ := dao.SysUser.Ctx(ctx).Where(g.Map{ + dao.SysUser.Columns().IsDeleted: 0, + dao.SysUser.Columns().Status: 1, + dao.SysUser.Columns().Id: messageInfo.ObjectId, + }).Count() + + if num == 0 { + err = gerror.New("用户不存在") + return + } + var messagereceive = new(entity.SysMessagereceive) + messagereceive.MessageId = int(lastInsertId) + messagereceive.UserId = messageInfo.ObjectId + messagereceive.IsRead = 0 + messagereceive.IsPush = 0 + messagereceive.IsDeleted = 0 + messagereceives = append(messagereceives, messagereceive) + } + if messagereceives != nil && len(messagereceives) > 0 { + var messagereceiveInput []*model.SysMessagereceiveInput + if err = gconv.Scan(messagereceives, &messagereceiveInput); err != nil { + return + } + //添加推送消息 + _, err = dao.SysMessagereceive.Ctx(ctx).Data(messagereceiveInput).Insert() + if err != nil { + return + } + } + return + }) + return +} + +// GetUnReadMessageAll 获取所有未读消息 +func (s *sSysMessage) GetUnReadMessageAll(ctx context.Context, input *model.MessageListDoInput) (total int, out []*model.MessageListOut, err error) { + m := g.Model(dao.SysMessagereceive.Table() + " l") + m = m.LeftJoin(dao.SysMessage.Table()+" s", "s."+dao.SysMessage.Columns().Id+"=l."+dao.SysMessagereceive.Columns().MessageId) + //获取当前用户信息 + loginUserId := service.Context().GetUserId(ctx) + m = m.Where("l."+dao.SysMessagereceive.Columns().UserId, loginUserId) + m = m.Where("l."+dao.SysMessagereceive.Columns().IsRead, 0) + m = m.Where("l."+dao.SysMessagereceive.Columns().IsDeleted, 0) + //获取总数 + total, err = m.Count() + if err != nil { + err = gerror.New("获取消息列表数据失败") + return + } + if input.PageNum == 0 { + input.PageNum = 1 + } + if input.PageSize == 0 { + input.PageSize = consts.PageSize + } + + err = m.Page(input.PageNum, input.PageSize).Fields("l.*").WithAll().OrderDesc(dao.SysMessage.Columns().CreatedAt).Scan(&out) + if err != nil { + err = gerror.New("获取消息列表失败") + return + } + return +} + +// GetUnReadMessageCount 获取所有未读消息数量 +func (s *sSysMessage) GetUnReadMessageCount(ctx context.Context) (out int, err error) { + m := dao.SysMessagereceive.Ctx(ctx) + m = m.LeftJoin(dao.SysMessage.Table(), "base_messagereceive.message_id = base_message.id") + //获取当前用户信息 + loginUserId := service.Context().GetUserId(ctx) + m = m.Where(dao.SysMessagereceive.Columns().UserId, loginUserId) + m = m.Where(dao.SysMessagereceive.Columns().IsRead, 0) + m = m.Where(dao.SysMessagereceive.Columns().IsDeleted, 0) + out, err = m.WithAll().Count() + if err != nil { + err = gerror.New("获取消息数量失败") + return + } + return +} + +// DelMessage 删除消息 +func (s *sSysMessage) DelMessage(ctx context.Context, ids []int) (err error) { + var memberreceives []*entity.SysMessagereceive + err = dao.SysMessagereceive.Ctx(ctx).Where(g.Map{ + dao.SysMessagereceive.Columns().IsDeleted: 0, + }).WhereIn(dao.SysMessagereceive.Columns().Id, ids).Scan(&memberreceives) + + for _, memberreceive := range memberreceives { + time, _ := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") + memberreceive.IsDeleted = 1 + memberreceive.DeletedAt = time + _, err = dao.SysMessagereceive.Ctx(ctx).Where(dao.SysMessagereceive.Columns().Id, memberreceive.Id).Data(memberreceive).Update() + if err != nil { + return + } + } + return +} + +// ClearMessage 一键清空消息 +func (s *sSysMessage) ClearMessage(ctx context.Context) (err error) { + time, _ := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") + _, err = dao.SysMessagereceive.Ctx(ctx).Data(g.Map{dao.SysMessagereceive.Columns().IsDeleted: 1, + dao.SysMessagereceive.Columns().DeletedAt: time}).Update() + if err != nil { + return + } + return +} + +// ReadMessage 阅读消息 +func (s *sSysMessage) ReadMessage(ctx context.Context, id int) (err error) { + var memberreceive *entity.SysMessagereceive + err = dao.SysMessagereceive.Ctx(ctx).Where(g.Map{ + dao.SysMessagereceive.Columns().Id: id, + dao.SysMessagereceive.Columns().IsDeleted: 0, + }).Scan(&memberreceive) + if memberreceive == nil { + err = gerror.New("ID错误") + return + } + time, err := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") + if err != nil { + return + } + memberreceive.IsRead = 1 + memberreceive.ReadTime = time + _, err = dao.SysMessagereceive.Ctx(ctx).Where(dao.SysMessagereceive.Columns().Id, id).Data(memberreceive).Update() + if err != nil { + return + } + return +} + +// ReadMessageAll 全部阅读消息 +func (s *sSysMessage) ReadMessageAll(ctx context.Context) (err error) { + loginUserId := service.Context().GetUserId(ctx) + time, err := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") + if err != nil { + return + } + _, err = dao.SysMessagereceive.Ctx(ctx).Where(dao.SysMessagereceive.Columns().UserId, loginUserId).Data(g.Map{ + dao.SysMessagereceive.Columns().IsRead: 1, + dao.SysMessagereceive.Columns().ReadTime: time, + }).Update() + if err != nil { + return + } + return +} + +// GetUnReadMessageLast 获取用户最后一条未读消息 +func (s *sSysMessage) GetUnReadMessageLast(ctx context.Context, userId int) (out []*model.MessageListOut, err error) { + //TODO 这个地方从缓存(Redis)中取 =============== + m := g.Model(dao.SysMessagereceive.Table() + " l") + m = m.LeftJoin(dao.SysMessage.Table()+" s", "s."+dao.SysMessage.Columns().Id+"=l."+dao.SysMessagereceive.Columns().MessageId) + //获取当前用户信息 + m = m.Where("l."+dao.SysMessagereceive.Columns().UserId, userId) + m = m.Where("l."+dao.SysMessagereceive.Columns().IsRead, 0) + m = m.Where("l."+dao.SysMessagereceive.Columns().IsDeleted, 0) + m = m.Where("l."+dao.SysMessagereceive.Columns().IsPush, 0) + + err = m.Fields("l.*").WithAll().Scan(&out) + if err != nil { + err = gerror.New("获取消息列表失败") + return + } + //修改消息 + var ids []int + if out != nil && len(out) > 0 { + for _, v := range out { + ids = append(ids, v.Id) + } + } + if len(ids) > 0 { + _, err = dao.SysMessagereceive.Ctx(ctx).Data(g.Map{ + dao.SysMessagereceive.Columns().IsPush: 1, + }).WhereIn(dao.SysMessagereceive.Columns().Id, ids).Update() + } + + return +} diff --git a/internal/logic/system/sys_notifications.go b/internal/logic/system/sys_notifications.go index 77d6040..1018682 100644 --- a/internal/logic/system/sys_notifications.go +++ b/internal/logic/system/sys_notifications.go @@ -4,11 +4,13 @@ import ( "context" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/api/v1/system" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" ) type sSysNotifications struct{} @@ -20,7 +22,7 @@ func init() { service.RegisterSysNotifications(sSysNotificationsNew()) } -// 获取列表数据 +// GetSysNotificationsList 获取列表数据 func (s *sSysNotifications) GetSysNotificationsList(ctx context.Context, input *model.GetNotificationsListInput) (total, page int, list []*model.NotificationsOut, err error) { err = g.Try(ctx, func(ctx context.Context) { m := dao.SysNotifications.Ctx(ctx) @@ -41,25 +43,32 @@ func (s *sSysNotifications) GetSysNotificationsList(ctx context.Context, input * return } -// 获取指定ID数据 +// GetSysNotificationsById 获取指定ID数据 func (s *sSysNotifications) GetSysNotificationsById(ctx context.Context, id int) (out *model.NotificationsRes, err error) { err = dao.SysNotifications.Ctx(ctx).Where("id", id).Scan(&out) return } -// 添加数据 +// AddSysNotifications 添加数据 func (s *sSysNotifications) AddSysNotifications(ctx context.Context, in model.NotificationsAddInput) (err error) { - _, err = dao.SysNotifications.Ctx(ctx).Insert(in) + _, err = dao.SysNotifications.Ctx(ctx).Data(do.SysNotifications{ + Title: in.Title, + Doc: in.Doc, + Source: in.Source, + Types: in.Types, + CreatedAt: gtime.Now(), + Status: in.Status, + }).Insert() return } -// 修改数据 +// EditSysNotifications 修改数据 func (s *sSysNotifications) EditSysNotifications(ctx context.Context, in model.NotificationsEditInput) (err error) { _, err = dao.SysNotifications.Ctx(ctx).FieldsEx(dao.SysNotifications.Columns().Id).Where(dao.SysNotifications.Columns().Id, in.Id).Update(in) return } -// 删除数据 +// DeleteSysNotifications 删除数据 func (s *sSysNotifications) DeleteSysNotifications(ctx context.Context, in *system.DeleteNotificationsReq) (err error) { _, err = dao.SysNotifications.Ctx(ctx).Delete(dao.SysNotifications.Columns().Id+" in (?)", in.Ids) return diff --git a/internal/logic/system/sys_oper_log.go b/internal/logic/system/sys_oper_log.go index 66ba95b..fd683fd 100644 --- a/internal/logic/system/sys_oper_log.go +++ b/internal/logic/system/sys_oper_log.go @@ -6,15 +6,18 @@ import ( "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/grpool" "github.com/gogf/gf/v2/os/gtime" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/utils" + "github.com/gogf/gf/v2/util/gconv" "net/url" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/utility/utils" "strings" ) @@ -75,7 +78,7 @@ func (s *sSysOperLog) GetList(ctx context.Context, input *model.SysOperLogDoInpu input.PageNum = 1 } if input.PageSize == 0 { - input.PageSize = consts.DefaultPageSize + input.PageSize = consts.PageSize } //获取操作日志列表信息 err = m.Page(input.PageNum, input.PageSize).OrderDesc(dao.SysOperLog.Columns().OperId).Scan(&out) @@ -193,7 +196,130 @@ func (s *sSysOperLog) Add(ctx context.Context, userId int, url *url.URL, param g operLogInfo.JsonResult = string(b) } } - _, err = dao.SysOperLog.Ctx(ctx).Data(operLogInfo).Insert() + _, err = dao.SysOperLog.Ctx(ctx).Data(do.SysOperLog{ + Title: operLogInfo.Title, + BusinessType: operLogInfo.BusinessType, + Method: operLogInfo.Method, + RequestMethod: operLogInfo.RequestMethod, + OperatorType: operLogInfo.OperatorType, + OperName: operLogInfo.OperName, + DeptName: operLogInfo.DeptName, + OperUrl: operLogInfo.OperUrl, + OperIp: operLogInfo.OperIp, + OperLocation: operLogInfo.OperLocation, + OperParam: operLogInfo.OperParam, + JsonResult: operLogInfo.JsonResult, + Status: operLogInfo.Status, + ErrorMsg: operLogInfo.ErrorMsg, + OperTime: operLogInfo.OperTime, + }).Insert() + return +} + +func (s *sSysOperLog) AnalysisLog(ctx context.Context) (data entity.SysOperLog) { + // 获取当前请求的上下文对象 + mctx := service.Context().Get(ctx) + request := ghttp.RequestFromCtx(ctx) + handlerResponse := request.GetHandlerResponse() // 响应结果 + param := request.GetMap() // 请求参数 + + res := gconv.Map(handlerResponse) + + //takeUpTime, ok := mctx.Data["request.takeUpTime"].(int64) + //if !ok { + // takeUpTime = 0 // 或适当的默认值 + //} + //g.Log().Debug(ctx, "request.takeUpTime: ", takeUpTime) + + operLogInfo := entity.SysOperLog{} + + if user := mctx.User; user != nil { + operLogInfo.OperName = user.UserName + + var deptInfo *entity.SysDept + err := dao.SysDept.Ctx(ctx).Where(g.Map{ + dao.SysDept.Columns().DeptId: user.DeptId, + dao.SysDept.Columns().IsDeleted: 0, + dao.SysDept.Columns().Status: 1, + }).Scan(&deptInfo) + if err == nil && deptInfo != nil { + operLogInfo.DeptName = deptInfo.DeptName + } + } + + operLogInfo.Method = request.URL.Path + apiInfo, _ := service.SysApi().GetInfoByAddress(ctx, request.URL.Path) + if apiInfo != nil { + operLogInfo.Title = apiInfo.Name + } + + operLogInfo.RequestMethod = request.Method + operLogInfo.OperatorType = 1 + + // 业务类型 + switch request.Method { + case "POST": + operLogInfo.BusinessType = 1 + case "PUT": + operLogInfo.BusinessType = 2 + case "DELETE": + operLogInfo.BusinessType = 3 + default: + operLogInfo.BusinessType = 0 + } + + rawQuery := "" + if rq := request.URL.RawQuery; rq != "" { + rawQuery = "?" + rq + } + operLogInfo.OperUrl = request.URL.Path + rawQuery + + operLogInfo.OperIp = utils.GetClientIp(request.GetCtx()) + operLogInfo.OperLocation = utils.GetCityByIp(operLogInfo.OperIp) + + operTime, err := gtime.StrToTimeFormat(gtime.Datetime(), "2006-01-02 15:04:05") + if err != nil { + g.Log().Error(ctx, "Failed to parse time: ", err) + return + } + operLogInfo.OperTime = operTime + + if param != nil { + if b, err := gjson.Encode(param); err == nil { + operLogInfo.OperParam = string(b) + } + } + + var code gcode.Code = gcode.CodeOK + if erro := request.GetError(); erro != nil { + code = gerror.Code(erro) + if code == gcode.CodeNil { + code = gcode.CodeInternalError + } + operLogInfo.Status = 0 + errorMsgMap := map[string]interface{}{ + "code": code.Code(), + "message": erro.Error(), + } + if errorMsg, err := gjson.Encode(errorMsgMap); err == nil { + operLogInfo.ErrorMsg = string(errorMsg) + } + } else { + operLogInfo.Status = 1 + if b, err := gjson.Encode(res); err == nil { + operLogInfo.JsonResult = string(b) + if len(operLogInfo.JsonResult) > 65535 { + operLogInfo.JsonResult = "数据过大,未记录" + } + } + } + + return operLogInfo +} + +// RealWrite 真实写入 +func (s *sSysOperLog) RealWrite(ctx context.Context, log entity.SysOperLog) (err error) { + _, err = dao.SysOperLog.Ctx(ctx).FieldsEx(dao.SysOperLog.Columns().OperId).Data(log).Insert() return } @@ -211,7 +337,7 @@ func (s *sSysOperLog) Detail(ctx context.Context, operId int) (entity *entity.Sy // Del 根据ID删除操作日志 func (s *sSysOperLog) Del(ctx context.Context, operIds []int) (err error) { for _, operId := range operIds { - var sysOperLog *entity.BaseDbLink + var sysOperLog *entity.SysOperLog _ = dao.SysOperLog.Ctx(ctx).Where(g.Map{ dao.SysOperLog.Columns().OperId: operId, }).Scan(&sysOperLog) diff --git a/internal/logic/system/sys_organization.go b/internal/logic/system/sys_organization.go index e43cdba..445c2ec 100644 --- a/internal/logic/system/sys_organization.go +++ b/internal/logic/system/sys_organization.go @@ -5,11 +5,14 @@ import ( "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sort" "strconv" "strings" "time" @@ -26,7 +29,7 @@ func init() { service.RegisterSysOrganization(SysOrganizationNew()) } -// GetList 获取组织数据 +// GetTree 获取组织数据 func (s *sSysOrganization) GetTree(ctx context.Context, name string, status int) (data []*model.OrganizationOut, err error) { orgainzationInfo, err := s.GetData(ctx, name, status) var parentNodeOut []*model.OrganizationOut @@ -38,10 +41,42 @@ func (s *sSysOrganization) GetTree(ctx context.Context, name string, status int) if err = gconv.Scan(v, &parentNode); err != nil { return } - parentNodeOut = append(parentNodeOut, parentNode) + + var isExist = false + for _, orgOut := range parentNodeOut { + if orgOut.Id == parentNode.Id { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } + } else { + //查找根节点 + parentOrg := FindOrgParentByChildrenId(ctx, int(v.ParentId)) + if err = gconv.Scan(parentOrg, &parentNode); err != nil { + return + } + var isExist = false + for _, orgOut := range parentNodeOut { + if orgOut.Id == parentOrg.Id { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } } } } + + //对父节点进行排序 + sort.SliceStable(parentNodeOut, func(i, j int) bool { + return parentNodeOut[i].OrderNum < parentNodeOut[j].OrderNum + }) + data = OrganizationTree(parentNodeOut, orgainzationInfo) return } @@ -60,11 +95,30 @@ func OrganizationTree(parentNodeOut []*model.OrganizationOut, data []*model.Orga parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) } } + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].OrderNum < v.Children[j].OrderNum + }) + OrganizationTree(v.Children, data) } return parentNodeOut } +// FindOrgParentByChildrenId 根据子节点获取岗位根节点 +func FindOrgParentByChildrenId(ctx context.Context, parentId int) *entity.SysOrganization { + var org *entity.SysOrganization + + _ = dao.SysOrganization.Ctx(ctx).Where(g.Map{ + dao.SysOrganization.Columns().Id: parentId, + }).Scan(&org) + + if org.ParentId != -1 { + return FindOrgParentByChildrenId(ctx, int(org.ParentId)) + } + return org +} + // GetData 执行获取数据操作 func (s *sSysOrganization) GetData(ctx context.Context, name string, status int) (data []*model.OrganizationOut, err error) { m := dao.SysOrganization.Ctx(ctx) @@ -75,8 +129,9 @@ func (s *sSysOrganization) GetData(ctx context.Context, name string, status int) if name != "" { m = m.WhereLike(dao.SysOrganization.Columns().Name, "%"+name+"%") } + err = m.Where(dao.SysOrganization.Columns().IsDeleted, 0). - OrderDesc(dao.SysOrganization.Columns().OrderNum). + OrderAsc(dao.SysOrganization.Columns().OrderNum). Scan(&data) if err != nil { return @@ -89,25 +144,59 @@ func (s *sSysOrganization) Add(ctx context.Context, input *model.AddOrganization //根据名称查看组织是否存在 organization := checkOrganizationName(ctx, input.Name, 0) if organization != nil { - return gerror.New("组织已存在,无法添加") + return gerror.New("区域已存在,无法添加") } - organization = new(entity.SysOrganization) - organization.Number = "org_" + strconv.FormatInt(time.Now().Unix(), 10) + /*organization = new(entity.SysOrganization)*/ + /*organization.Number = "org_" + strconv.FormatInt(time.Now().Unix(), 10)*/ + + //获取上级组织信息 + if input.ParentId != -1 { + var parentOrg *entity.SysOrganization + parentOrg, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentOrg == nil { + err = gerror.Newf("无权限选择当前区域") + return + } + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) organization = new(entity.SysOrganization) - if err := gconv.Scan(input, &organization); err != nil { + if err = gconv.Scan(input, &organization); err != nil { return err } - organization.IsDeleted = 0 - organization.CreatedBy = uint(loginUserId) + /*organization.IsDeleted = 0 + organization.CreatedBy = uint(loginUserId)*/ //开启事务管理 - err = dao.SysOrganization.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { - lastId, err1 := dao.SysOrganization.Ctx(ctx).Data(organization).InsertAndGetId() - if err1 != nil { - return err1 + err = dao.SysOrganization.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + result, err := dao.SysOrganization.Ctx(ctx).Data(do.SysOrganization{ + DeptId: service.Context().GetUserDeptId(ctx), + ParentId: organization.ParentId, + Ancestors: organization.Ancestors, + Name: organization.Name, + Number: "org_" + strconv.FormatInt(time.Now().Unix(), 10), + OrderNum: organization.OrderNum, + Leader: organization.Leader, + Phone: organization.Phone, + Email: organization.Email, + Status: organization.Status, + IsDeleted: 0, + CreatedAt: gtime.Now(), + CreatedBy: uint(loginUserId), + }).Insert() + if err != nil { + return + } + //获取主键ID + lastInsertId, err := service.Sequences().GetSequences(ctx, result, dao.SysOrganization.Table(), dao.SysOrganization.Columns().Id) + if err != nil { + return } - err = setOrganizationAncestors(ctx, input.ParentId, lastId) + + err = setOrganizationAncestors(ctx, input.ParentId, lastInsertId) if err != nil { return err } @@ -118,26 +207,42 @@ func (s *sSysOrganization) Add(ctx context.Context, input *model.AddOrganization // Edit 修改组织 func (s *sSysOrganization) Edit(ctx context.Context, input *model.EditOrganizationInput) (err error) { + if input.Id == input.ParentId { + return gerror.New("父级不能为自己") + } var organization1, organization2 *entity.SysOrganization //根据ID查看组织是否存在 organization1 = checkOrganizationId(ctx, input.Id, organization1) organization := organization1.ParentId organizationAnces := organization1.Ancestors if organization1 == nil { - return gerror.New("组织不存在") + return gerror.New("区域不存在") } organization2 = checkOrganizationName(ctx, input.Name, input.Id) if organization2 != nil { - return gerror.New("相同组织已存在,无法修改") + return gerror.New("相同区域已存在,无法修改") + } + //判断上级组织是否可以选择 + if input.ParentId != -1 { + var parentOrg *entity.SysOrganization + parentOrg, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentOrg == nil { + err = gerror.Newf("无权限选择区域") + return + } } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - if err := gconv.Scan(input, &organization1); err != nil { + if err = gconv.Scan(input, &organization1); err != nil { return err } organization1.UpdatedBy = loginUserId //开启事务管理 - err = dao.SysOrganization.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysOrganization.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { _, err = dao.SysOrganization.Ctx(ctx).Data(organization1). Where(dao.SysOrganization.Columns().Id, input.Id).Update() if err != nil { @@ -191,12 +296,11 @@ func (s *sSysOrganization) Edit(ctx context.Context, input *model.EditOrganizati // Detail 组织详情 func (s *sSysOrganization) Detail(ctx context.Context, id int64) (entity *entity.SysOrganization, err error) { - _ = dao.SysOrganization.Ctx(ctx).Where(g.Map{ + m := dao.SysOrganization.Ctx(ctx) + + _ = m.Where(g.Map{ dao.SysOrganization.Columns().Id: id, }).Scan(&entity) - if entity == nil { - return nil, gerror.New("ID错误") - } return } @@ -220,6 +324,7 @@ func (s *sSysOrganization) Del(ctx context.Context, id int64) (err error) { if num > 0 { return gerror.New("请先删除子节点!") } + loginUserId := service.Context().GetUserId(ctx) //更新组织信息 _, err = dao.SysOrganization.Ctx(ctx). @@ -236,7 +341,9 @@ func (s *sSysOrganization) Del(ctx context.Context, id int64) (err error) { // GetAll 获取全部组织数据 func (s *sSysOrganization) GetAll(ctx context.Context) (data []*entity.SysOrganization, err error) { - err = dao.SysOrganization.Ctx(ctx).Where(g.Map{ + m := dao.SysOrganization.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysOrganization.Columns().Status: 1, dao.SysOrganization.Columns().IsDeleted: 0, }).Scan(&data) @@ -272,7 +379,10 @@ func setOrganizationAncestors(ctx context.Context, ParentId int64, lastId int64) // Count 获取组织数量 func (s *sSysOrganization) Count(ctx context.Context) (count int, err error) { - count, _ = dao.SysOrganization.Ctx(ctx).Where(g.Map{ + + m := dao.SysOrganization.Ctx(ctx) + + count, _ = m.Where(g.Map{ dao.SysOrganization.Columns().IsDeleted: 0, }).Count() return diff --git a/internal/logic/system/sys_plugins.go b/internal/logic/system/sys_plugins.go index 59955fe..dd2314e 100644 --- a/internal/logic/system/sys_plugins.go +++ b/internal/logic/system/sys_plugins.go @@ -2,13 +2,23 @@ package system import ( "context" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "path/filepath" + "regexp" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/plugins" + "sagooiot/pkg/utility/utils" + "strings" ) type sSysPlugins struct{} @@ -21,54 +31,250 @@ func init() { } // GetSysPluginsList 获取列表数据 -func (s *sSysPlugins) GetSysPluginsList(ctx context.Context, in *model.GetSysPluginsListInput) (total, page int, list []*model.SysPluginsOutput, err error) { - err = g.Try(ctx, func(ctx context.Context) { - m := dao.SysPlugins.Ctx(ctx) - - if in.KeyWord != "" { - m = m.WhereLike(dao.SysPlugins.Columns().Name, "%"+in.KeyWord+"%") - m = m.WhereLike(dao.SysPlugins.Columns().Title, "%"+in.KeyWord+"%") - m = m.WhereLike(dao.SysPlugins.Columns().Intro, "%"+in.KeyWord+"%") - } +func (s *sSysPlugins) GetSysPluginsList(ctx context.Context, in *model.GetSysPluginsListInput) (total, page int, list []*model.GetSysPluginsListOut, err error) { + m := dao.SysPlugins.Ctx(ctx) - total, err = m.Count() - if err != nil { - err = gerror.New("获取总行数失败") - return - } - page = in.PageNum - if in.PageSize == 0 { - in.PageSize = consts.PageSize - } - err = m.Page(page, in.PageSize).Order("start_time desc").Scan(&list) - if err != nil { - err = gerror.New("获取数据失败") - } - }) + if in.KeyWord != "" { + m = m.WhereLike(dao.SysPlugins.Columns().Name, "%"+in.KeyWord+"%") + m = m.WhereLike(dao.SysPlugins.Columns().Title, "%"+in.KeyWord+"%") + m = m.WhereLike(dao.SysPlugins.Columns().Description, "%"+in.KeyWord+"%") + } + + if in.Status > 0 { + m = m.Where(dao.SysPlugins.Columns().Status, in.Status) + } + + total, err = m.Count() + if err != nil { + err = gerror.New("获取总行数失败") + return + } + page = in.PageNum + if in.PageSize == 0 { + in.PageSize = consts.PageSize + } + err = m.Page(page, in.PageSize).OrderDesc(dao.SysPlugins.Columns().CreatedAt).Scan(&list) + if err != nil { + err = gerror.New("获取数据失败") + } return } // GetSysPluginsById 获取指定ID数据 -func (s *sSysPlugins) GetSysPluginsById(ctx context.Context, id int) (out *model.SysPluginsOutput, err error) { +func (s *sSysPlugins) GetSysPluginsById(ctx context.Context, id int) (out *entity.SysPlugins, err error) { err = dao.SysPlugins.Ctx(ctx).Where(dao.SysPlugins.Columns().Id, id).Scan(&out) return } +// GetSysPluginsByName 根据名称获取插件数据 +func (s *sSysPlugins) GetSysPluginsByName(ctx context.Context, name string) (out *entity.SysPlugins, err error) { + err = dao.SysPlugins.Ctx(ctx).Where(dao.SysPlugins.Columns().Name, name).Scan(&out) + return +} + +// GetSysPluginsByTitle 根据TITLE获取插件数据 +func (s *sSysPlugins) GetSysPluginsByTitle(ctx context.Context, title string) (out *entity.SysPlugins, err error) { + err = dao.SysPlugins.Ctx(ctx).Where(dao.SysPlugins.Columns().Title, title).Scan(&out) + return +} + // AddSysPlugins 添加数据 -func (s *sSysPlugins) AddSysPlugins(ctx context.Context, in model.SysPluginsAddInput) (err error) { - _, err = dao.SysPlugins.Ctx(ctx).Insert(in) +func (s *sSysPlugins) AddSysPlugins(ctx context.Context, file *ghttp.UploadFile) (err error) { + + srcFileName := file.Filename + ext := filepath.Ext(srcFileName) + if ext != ".zip" && ext != ".zipx" { + err = gerror.New("只允许上传zip或zipx文件!") + return + } + + //获取文件名(不包括后缀) + fileNameWithoutExt := srcFileName[:len(srcFileName)-len(filepath.Ext(srcFileName))] + + pattern := `^[^\p{Han}]*$` + + match, err := regexp.MatchString(pattern, fileNameWithoutExt) + if err != nil { + err = gerror.New("正则表达式匹配失败") + return + } + + if !match { + err = gerror.New("文件名请用英文命名") + return + } + //读取压缩包内指定文件内容 + result, err := utils.ReadZipFileByFileName(file, "info.json") + if err != nil { + return err + } + var plugins *entity.SysPlugins + if err = gconv.Scan(result, &plugins); err != nil { + return + } + if plugins.Types == "" { + err = gerror.New("插件类型不能为空") + return + } + if plugins.HandleType == "" { + err = gerror.New("处理方式类型不能为空") + return + } + if plugins.Name == "" { + err = gerror.New("名称不能为空") + return + } + if plugins.Title == "" { + err = gerror.New("标题不能为空") + return + } + //针对执行程序参数做特殊处理 + var args = result["args"].([]interface{}) + var argsArr []string + for _, v := range args { + a, _ := v.(string) + + argsArr = append(argsArr, a) + } + plugins.Args = strings.Join(argsArr, ",") + //对plugins struct赋值 + frontend, _ := result["frontend"].(map[string]interface{}) + + plugins.FrontendUi = 0 + if gconv.Bool(frontend["ui"]) { + plugins.FrontendUi = 1 + } + + plugins.FrontendUrl = frontend["url"].(string) + + plugins.FrontendConfiguration = 0 + if gconv.Bool(frontend["configuration"]) { + plugins.FrontendConfiguration = 1 + } + //判断名称是否重复 + pluginByName, _ := s.GetSysPluginsByName(ctx, plugins.Name) + if pluginByName != nil { + return gerror.New("插件名称不能重复") + } + //判断标题是否重复 + pluginByTitle, _ := s.GetSysPluginsByTitle(ctx, plugins.Title) + if pluginByTitle != nil { + return gerror.New("插件标题不能重复") + } + + err = dao.SysPlugins.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + //添加插件 + _, err = dao.SysPlugins.Ctx(ctx).Data(do.SysPlugins{ + DeptId: service.Context().GetUserDeptId(ctx), + Types: plugins.Types, + HandleType: plugins.HandleType, + Name: plugins.Name, + Title: plugins.Title, + Description: plugins.Description, + Version: plugins.Version, + Author: plugins.Author, + Icon: plugins.Icon, + Link: plugins.Link, + Command: plugins.Command, + Args: plugins.Args, + Status: 0, + FrontendUi: plugins.FrontendUi, + FrontendUrl: plugins.FrontendUrl, + FrontendConfiguration: plugins.FrontendConfiguration, + StartTime: plugins.StartTime, + IsDeleted: 0, + CreatedBy: uint(service.Context().GetUserId(ctx)), + CreatedAt: gtime.Now(), + }).Insert() + if err != nil { + err = gerror.New("添加插件表失败!") + return + } + //上传插件 + pluginsPath := g.Cfg().MustGet(context.Background(), "system.pluginsPath").String() + excludeFiles := []string{"info.json"} // 需要排除的文件名列表 + err = utils.UploadZip(file, pluginsPath, excludeFiles) + if err != nil { + return + } + return + }) + return } +func shouldExclude(filename string, excludeList []string) bool { + baseName := filepath.Base(filename) + + for _, excludedFile := range excludeList { + if baseName == excludedFile { + return true + } + } + + return false +} + // EditSysPlugins 修改数据 -func (s *sSysPlugins) EditSysPlugins(ctx context.Context, in model.SysPluginsEditInput) (err error) { - _, err = dao.SysPlugins.Ctx(ctx).FieldsEx(dao.SysPlugins.Columns().Id).Where(dao.SysPlugins.Columns().Id, in.Id).Update(in) +func (s *sSysPlugins) EditSysPlugins(ctx context.Context, input *model.SysPluginsEditInput) (err error) { + //根据ID查询 + var sp *entity.SysPlugins + err = dao.SysPlugins.Ctx(ctx).Where(dao.SysPlugins.Columns().Id, input.Id).Scan(&sp) + if sp == nil { + err = gerror.New("ID错误") + return + } + + //判断名称是否重复 + pluginByName, _ := s.GetSysPluginsByName(ctx, input.Name) + if pluginByName != nil && pluginByName.Id != sp.Id { + return gerror.New("插件名称不能重复") + } + //判断标题是否重复 + pluginByTitle, _ := s.GetSysPluginsByTitle(ctx, input.Title) + if pluginByTitle != nil && pluginByTitle.Id != sp.Id { + return gerror.New("插件标题不能重复") + } + + sp.Types = input.Types + sp.HandleType = input.HandleType + sp.Name = input.Name + sp.Title = input.Title + sp.Description = input.Description + sp.Version = input.Version + sp.Author = input.Author //strings.Join(input.Author, ",") + sp.Icon = input.Icon + sp.Link = input.Link + sp.Command = input.Command + sp.Args = strings.Join(input.Args, ",") + sp.FrontendUi = input.FrontendUi + sp.FrontendUrl = input.FrontendUrl + sp.FrontendConfiguration = input.FrontendConfiguration + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + sp.UpdatedBy = loginUserId + sp.UpdatedAt = gtime.Now() + _, err = dao.SysPlugins.Ctx(ctx).Data(sp).Where(dao.SysPlugins.Columns().Id, sp.Id).Update() return } // DeleteSysPlugins 删除数据 -func (s *sSysPlugins) DeleteSysPlugins(ctx context.Context, Ids []int) (err error) { - _, err = dao.SysPlugins.Ctx(ctx).Delete(dao.SysPlugins.Columns().Id+" in (?)", Ids) +func (s *sSysPlugins) DeleteSysPlugins(ctx context.Context, ids []int) (err error) { + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + for _, id := range ids { + plugin, _ := s.GetSysPluginsById(ctx, id) + if plugin == nil { + return gerror.New("ID错误,请重新选择!") + } + _, err = dao.SysPlugins.Ctx(ctx).Data(g.Map{ + dao.SysPlugins.Columns().Status: 0, + dao.SysPlugins.Columns().IsDeleted: 1, + dao.SysPlugins.Columns().DeletedBy: loginUserId, + dao.SysPlugins.Columns().DeletedAt: gtime.Now(), + }).Where(dao.SysPlugins.Columns().Id, id).Update() + } return } @@ -83,17 +289,62 @@ func (s *sSysPlugins) SaveSysPlugins(ctx context.Context, in model.SysPluginsAdd _, err = dao.SysPlugins.Ctx(ctx).Data(in).Where(req).Update() } else { - _, err = dao.SysPlugins.Ctx(ctx).Insert(in) + //获取当前登录用户ID + loginUserId := service.Context().GetUserId(ctx) + + _, err = dao.SysPlugins.Ctx(ctx).Data(do.SysPlugins{ + DeptId: service.Context().GetUserDeptId(ctx), + Types: in.Types, + HandleType: in.HandleType, + Name: in.Name, + Title: in.Title, + Description: in.Description, + Version: in.Version, + Author: in.Author, + Icon: in.Icon, + Link: in.Link, + Command: in.Command, + Args: in.Args, + Status: in.Status, + FrontendUi: in.FrontendUi, + FrontendUrl: in.FrontendUrl, + FrontendConfiguration: in.FrontendConfiguration, + StartTime: in.StartTime, + IsDeleted: 0, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() } return } func (s *sSysPlugins) EditStatus(ctx context.Context, id int, status int) (err error) { - //todo 进行插件的启停 + //获取当前登录用户ID + var pluginObj *entity.SysPlugins + err = dao.SysPlugins.Ctx(ctx).Where(dao.SysPlugins.Columns().Id, id).Scan(&pluginObj) + if err != nil { + err = gerror.New("获取插件信息失败") + return + } + switch status { case 0: + if pluginObj.Types == "notice" { + //关闭通知插件 + err = plugins.GetNoticePlugin().StopPlugin(pluginObj.Name) + } else { + //关闭协议插件 + err = plugins.GetProtocolPlugin().StopPlugin(pluginObj.Name) + } case 1: + if pluginObj.Types == "notice" { + //启动通知插件 + err = plugins.GetNoticePlugin().StartPlugin(pluginObj.Name) + } else { + //启动协议插件 + err = plugins.GetProtocolPlugin().StartPlugin(pluginObj.Name) + } } _, err = dao.SysPlugins.Ctx(ctx).Data("status", status).Where(dao.SysPlugins.Columns().Id, id).Update() return @@ -108,16 +359,13 @@ func (s *sSysPlugins) GetSysPluginsTypesAll(ctx context.Context, types string) ( dao.SysPlugins.Columns().IsDeleted: 0, }).WhereIn(dao.SysPlugins.Columns().Types, types) - //根据数据权限过滤数据 - m, _ = service.SysAuthorize().FilterDataByPermissions(ctx, m) - //获取当前登录用户ID - var plugins []*entity.SysPlugins - err = m.Scan(&plugins) + var sp []*entity.SysPlugins + err = m.Scan(&sp) if err != nil { return } - for _, plugin := range plugins { + for _, plugin := range sp { var sysPlugins = new(model.SysPluginsInfoOut) sysPlugins.Types = plugin.Types sysPlugins.HandleType = plugin.HandleType diff --git a/internal/logic/system/sys_plugins_config.go b/internal/logic/system/sys_plugins_config.go index 9f421f6..f8ca39f 100644 --- a/internal/logic/system/sys_plugins_config.go +++ b/internal/logic/system/sys_plugins_config.go @@ -6,11 +6,12 @@ import ( "github.com/gogf/gf/v2/encoding/gyaml" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gctx" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sSystemPluginsConfig struct{} @@ -22,7 +23,7 @@ func init() { service.RegisterSystemPluginsConfig(sSystemPluginsConfigNew()) } -//GetPluginsConfigList 获取列表数据 +// GetPluginsConfigList 获取列表数据 func (s *sSystemPluginsConfig) GetPluginsConfigList(ctx context.Context, in *model.GetPluginsConfigListInput) (total, page int, list []*model.PluginsConfigOutput, err error) { err = g.Try(ctx, func(ctx context.Context) { m := dao.SysPluginsConfig.Ctx(ctx) @@ -35,7 +36,7 @@ func (s *sSystemPluginsConfig) GetPluginsConfigList(ctx context.Context, in *mod if in.PageSize == 0 { in.PageSize = consts.PageSize } - err = m.Page(page, in.PageSize).Order("created_at desc").Scan(&list) + err = m.Page(page, in.PageSize).Scan(&list) if err != nil { err = gerror.New("获取数据失败") } @@ -43,13 +44,13 @@ func (s *sSystemPluginsConfig) GetPluginsConfigList(ctx context.Context, in *mod return } -//GetPluginsConfigById 获取指定ID数据 +// GetPluginsConfigById 获取指定ID数据 func (s *sSystemPluginsConfig) GetPluginsConfigById(ctx context.Context, id int) (out *model.PluginsConfigOutput, err error) { err = dao.SysPluginsConfig.Ctx(ctx).Where(dao.SysPluginsConfig.Columns().Id, id).Scan(&out) return } -//GetPluginsConfigByName 获取指定ID数据 +// GetPluginsConfigByName 获取指定ID数据 func (s *sSystemPluginsConfig) GetPluginsConfigByName(ctx context.Context, types, name string) (out *model.PluginsConfigOutput, err error) { var reqData = g.Map{ dao.SysPluginsConfig.Columns().Type: types, @@ -59,19 +60,28 @@ func (s *sSystemPluginsConfig) GetPluginsConfigByName(ctx context.Context, types return } -//AddPluginsConfig 添加数据 +// AddPluginsConfig 添加数据 func (s *sSystemPluginsConfig) AddPluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) { - _, err = dao.SysPluginsConfig.Ctx(ctx).Insert(in) + _, err = dao.SysPluginsConfig.Ctx(ctx).Data(do.SysPluginsConfig{ + Type: in.Type, + Name: in.Name, + Value: in.Value, + Doc: in.Doc, + }).Insert() + err = s.updateCache(ctx, in.Type, in.Name, in.Value) + return } -//EditPluginsConfig 修改数据 +// EditPluginsConfig 修改数据 func (s *sSystemPluginsConfig) EditPluginsConfig(ctx context.Context, in model.PluginsConfigEditInput) (err error) { _, err = dao.SysPluginsConfig.Ctx(ctx).FieldsEx(dao.SysPluginsConfig.Columns().Id).Where(dao.SysPluginsConfig.Columns().Id, in.Id).Update(in) + err = s.updateCache(ctx, in.Type, in.Name, in.Value) + return } -//SavePluginsConfig 更新数据,有数据就修改,没有数据就添加 +// SavePluginsConfig 更新数据,有数据就修改,没有数据就添加 func (s *sSystemPluginsConfig) SavePluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) { var reqData = g.Map{ dao.SysPluginsConfig.Columns().Id: in.Type, @@ -86,24 +96,20 @@ func (s *sSystemPluginsConfig) SavePluginsConfig(ctx context.Context, in model.P return } -//DeletePluginsConfig 删除数据 +// DeletePluginsConfig 删除数据 func (s *sSystemPluginsConfig) DeletePluginsConfig(ctx context.Context, Ids []int) (err error) { _, err = dao.SysPluginsConfig.Ctx(ctx).Delete(dao.SysPluginsConfig.Columns().Id+" in (?)", Ids) return } -//UpdateAllPluginsConfigCache 将插件数据更新到缓存 -func (s *sSystemPluginsConfig) UpdateAllPluginsConfigCache() (err error) { +// UpdateAllPluginsConfigCache 将插件数据更新到缓存 +func (s *sSystemPluginsConfig) UpdateAllPluginsConfigCache(ctx context.Context) (err error) { var dataList []*model.PluginsConfigOutput err = dao.SysPluginsConfig.Ctx(context.TODO()).Scan(&dataList) if err != nil { return } - - var ( - ctx = gctx.New() - ) for _, datum := range dataList { err = s.updateCache(ctx, datum.Type, datum.Name, datum.Value) } @@ -111,21 +117,19 @@ func (s *sSystemPluginsConfig) UpdateAllPluginsConfigCache() (err error) { } func (s *sSystemPluginsConfig) updateCache(ctx context.Context, pluginsType, name, value string) (err error) { - - //缓存的key的规则是:"plugins+插件类型+插件名称 - key := "plugins" + pluginsType + name - _, err = g.Redis().Do(ctx, "SET", key, value) + key := fmt.Sprintf(consts.PluginsTypeName, pluginsType, name) + err = cache.Instance().Set(ctx, key, value, 0) return } -//GetPluginsConfigData 获取列表数据 +// GetPluginsConfigData 获取列表数据 func (s *sSystemPluginsConfig) GetPluginsConfigData(pluginType, pluginName string) (res map[interface{}]interface{}, err error) { - key := "plugins" + pluginType + pluginName - fmt.Println(key) - pcgData, err := g.Redis().Do(context.TODO(), "GET", key) - + key := fmt.Sprintf(consts.PluginsTypeName, pluginType, pluginName) + pcgData, err := cache.Instance().Get(context.Background(), key) + if err != nil { + return + } err = gyaml.DecodeTo([]byte(pcgData.String()), &res) - return } diff --git a/internal/logic/system/sys_post.go b/internal/logic/system/sys_post.go index f43c2bd..78d43b3 100644 --- a/internal/logic/system/sys_post.go +++ b/internal/logic/system/sys_post.go @@ -2,11 +2,14 @@ package system import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "errors" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sort" "time" "github.com/gogf/gf/v2/errors/gerror" @@ -38,10 +41,39 @@ func (s *sSysPost) GetTree(ctx context.Context, postName string, postCode string if err = gconv.Scan(v, &parentNode); err != nil { return } - parentNodeOut = append(parentNodeOut, parentNode) + var isExist = false + for _, postOut := range parentNodeOut { + if postOut.PostId == parentNode.PostId { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } + } else { + //查找根节点 + parentPost := FindPostParentByChildrenId(ctx, int(v.ParentId)) + if err = gconv.Scan(parentPost, &parentNode); err != nil { + return + } + var isExist = false + for _, postOut := range parentNodeOut { + if postOut.PostId == int64(parentPost.PostId) { + isExist = true + break + } + } + if !isExist { + parentNodeOut = append(parentNodeOut, parentNode) + } } } } + //对父节点进行排序 + sort.SliceStable(parentNodeOut, func(i, j int) bool { + return parentNodeOut[i].PostSort < parentNodeOut[j].PostSort + }) data = postTree(parentNodeOut, postInfo) if len(data) == 0 { if err = gconv.Scan(postInfo, &data); err != nil { @@ -69,11 +101,29 @@ func postTree(parentNodeOut []*model.PostOut, data []*model.PostOut) (dataTree [ parentNodeOut[k].Children = append(parentNodeOut[k].Children, node) } } + //对子节点进行排序 + sort.SliceStable(v.Children, func(i, j int) bool { + return v.Children[i].PostSort < v.Children[j].PostSort + }) postTree(v.Children, data) } return parentNodeOut } +// FindPostParentByChildrenId 根据子节点获取岗位根节点 +func FindPostParentByChildrenId(ctx context.Context, parentId int) *entity.SysPost { + var post *entity.SysPost + + _ = dao.SysPost.Ctx(ctx).Where(g.Map{ + dao.SysPost.Columns().PostId: parentId, + }).Scan(&post) + + if post.ParentId != -1 { + return FindPostParentByChildrenId(ctx, post.ParentId) + } + return post +} + // Add 添加岗位 func (s *sSysPost) Add(ctx context.Context, input *model.AddPostInput) (err error) { var post *entity.SysPost @@ -82,16 +132,40 @@ func (s *sSysPost) Add(ctx context.Context, input *model.AddPostInput) (err erro if post != nil { return gerror.New("岗位已存在,无法添加") } + //获取上级岗位信息 + if input.ParentId != -1 { + var parentPost *entity.SysPost + parentPost, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentPost == nil { + err = gerror.Newf("无权限选择当前岗位") + return + } + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) post = new(entity.SysPost) - if err := gconv.Scan(input, &post); err != nil { + if err = gconv.Scan(input, &post); err != nil { return err } post.PostCode = "G" + time.Now().Format("20060102150405") post.IsDeleted = 0 post.CreatedBy = uint(loginUserId) - _, err = dao.SysPost.Ctx(ctx).Data(post).Insert() + _, err = dao.SysPost.Ctx(ctx).Data(do.SysPost{ + DeptId: service.Context().GetUserDeptId(ctx), + ParentId: post.ParentId, + PostCode: post.PostCode, + PostName: post.PostName, + PostSort: post.PostSort, + Status: post.Status, + Remark: post.Remark, + IsDeleted: 0, + CreatedBy: post.CreatedBy, + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return err } @@ -100,6 +174,9 @@ func (s *sSysPost) Add(ctx context.Context, input *model.AddPostInput) (err erro // Edit 修改岗位 func (s *sSysPost) Edit(ctx context.Context, input *model.EditPostInput) (err error) { + if input.PostId == input.ParentId { + return gerror.New("父级不能为自己") + } var post, post2 *entity.SysPost //根据ID查看岗位是否存在 post = checkPostId(ctx, input.PostId, post) @@ -110,12 +187,25 @@ func (s *sSysPost) Edit(ctx context.Context, input *model.EditPostInput) (err er if post2 != nil { return gerror.New("相同岗位已存在,无法修改") } - //获取当前登录用户ID - loginUserId := service.Context().GetUserId(ctx) - if err := gconv.Scan(input, &post); err != nil { + + //判断上级岗位是否可以选择 + if input.ParentId != -1 { + var parentPost *entity.SysPost + parentPost, err = s.Detail(ctx, input.ParentId) + if err != nil { + return + } + if parentPost == nil { + err = gerror.Newf("无权限选择岗位") + return + } + } + + if err = gconv.Scan(input, &post); err != nil { return err } - post.UpdatedBy = uint(loginUserId) + post.UpdatedBy = uint(service.Context().GetUserId(ctx)) + post.UpdatedAt = gtime.Now() //开启事务管理 _, err = dao.SysPost.Ctx(ctx).Data(post). Where(dao.SysPost.Columns().PostId, input.PostId).Update() @@ -127,12 +217,11 @@ func (s *sSysPost) Edit(ctx context.Context, input *model.EditPostInput) (err er // Detail 岗位详情 func (s *sSysPost) Detail(ctx context.Context, postId int64) (entity *entity.SysPost, err error) { - _ = dao.SysPost.Ctx(ctx).Where(g.Map{ + m := dao.SysPost.Ctx(ctx) + + _ = m.Where(g.Map{ dao.SysPost.Columns().PostId: postId, }).Scan(&entity) - if entity == nil { - return nil, gerror.New("ID错误") - } return } @@ -150,8 +239,9 @@ func (s *sSysPost) GetData(ctx context.Context, postName string, postCode string if status != -1 { m = m.Where(dao.SysPost.Columns().Status, status) } + err = m.Where(dao.SysPost.Columns().IsDeleted, 0). - OrderDesc(dao.SysPost.Columns().PostSort). + OrderAsc(dao.SysPost.Columns().PostSort). Scan(&data) if err != nil { return @@ -192,17 +282,16 @@ func (s *sSysPost) Del(ctx context.Context, postId int64) (err error) { if num > 0 { return gerror.New("请先删除子节点!") } + loginUserId := service.Context().GetUserId(ctx) //更新岗位信息 _, err = dao.SysPost.Ctx(ctx). Data(g.Map{ dao.SysPost.Columns().DeletedBy: uint(loginUserId), + dao.SysPost.Columns().DeletedAt: gtime.Now(), dao.SysPost.Columns().IsDeleted: 1, }).Where(dao.SysPost.Columns().PostId, postId). Update() - //删除岗位信息 - _, err = dao.SysPost.Ctx(ctx).Where(dao.SysPost.Columns().PostId, postId). - Delete() return } @@ -216,11 +305,12 @@ func checkPostId(ctx context.Context, PostId int64, post *entity.SysPost) *entit } // GetUsedPost 获取正常状态的岗位 -func (s *sSysPost) GetUsedPost(ctx context.Context) (list []*model.DetailPostRes, err error) { - err = g.Try(ctx, func(ctx context.Context) { - err = dao.SysPost.Ctx(ctx).Where(dao.SysPost.Columns().Status, 1). - Order(dao.SysPost.Columns().PostSort + " ASC, " + dao.SysPost.Columns().PostId + " ASC ").Scan(&list) - liberr.ErrIsNil(ctx, err, "获取岗位数据失败") - }) +func (s *sSysPost) GetUsedPost(ctx context.Context) (list []*model.DetailPostOut, err error) { + err = dao.SysPost.Ctx(ctx).Where(dao.SysPost.Columns().Status, 1). + Order(dao.SysPost.Columns().PostSort + " ASC, " + dao.SysPost.Columns().PostId + " ASC ").Scan(&list) + if err != nil { + return nil, errors.New("获取岗位失败") + } + return } diff --git a/internal/logic/system/sys_role.go b/internal/logic/system/sys_role.go index 1c689dc..267f331 100644 --- a/internal/logic/system/sys_role.go +++ b/internal/logic/system/sys_role.go @@ -2,17 +2,19 @@ package system import ( "context" + "errors" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" "strings" ) @@ -29,7 +31,9 @@ func sysRoleNew() *sSysRole { // GetAll 获取所有的角色 func (s *sSysRole) GetAll(ctx context.Context) (entity []*entity.SysRole, err error) { - err = dao.SysRole.Ctx(ctx).Where(g.Map{ + m := dao.SysRole.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysRole.Columns().Status: 1, dao.SysRole.Columns().IsDeleted: 0, }).OrderAsc(dao.SysRole.Columns().ListOrder).Scan(&entity) @@ -50,7 +54,7 @@ func (s *sSysRole) GetTree(ctx context.Context, name string, status int) (out [] err = m.OrderAsc(dao.SysRole.Columns().ListOrder).Scan(&e) if len(e) > 0 { - out, err = GetRoleTree(e) + out, err = GetRoleTree(ctx, e) if err != nil { return } @@ -69,6 +73,16 @@ func (s *sSysRole) Add(ctx context.Context, input *model.AddRoleInput) (err erro if role != nil { return gerror.New("角色已存在,无法添加") } + //判断是否有权限删除当前角色 + if input.ParentId != -1 { + var parentRole *entity.SysRole + parentRole, err = s.GetInfoById(ctx, uint(input.ParentId)) + if parentRole == nil { + err = gerror.Newf("无权限选择当前角色") + return + } + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) role = new(entity.SysRole) @@ -79,8 +93,19 @@ func (s *sSysRole) Add(ctx context.Context, input *model.AddRoleInput) (err erro role.Remark = input.Remark role.Status = input.Status role.IsDeleted = 0 - role.CreateBy = uint(loginUserId) - _, err = dao.SysRole.Ctx(ctx).Data(role).Insert() + role.CreatedBy = uint(loginUserId) + _, err = dao.SysRole.Ctx(ctx).Data(do.SysRole{ + DeptId: service.Context().GetUserDeptId(ctx), + ParentId: role.ParentId, + ListOrder: role.ListOrder, + Name: role.Name, + DataScope: role.DataScope, + Remark: role.Remark, + Status: role.Status, + IsDeleted: role.IsDeleted, + CreatedBy: role.CreatedBy, + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return err } @@ -89,6 +114,9 @@ func (s *sSysRole) Add(ctx context.Context, input *model.AddRoleInput) (err erro // Edit 编辑 func (s *sSysRole) Edit(ctx context.Context, input *model.EditRoleInput) (err error) { + if input.Id == uint(input.ParentId) { + return gerror.New("父级不能为自己") + } var role *entity.SysRole //根据ID查询角色是否存在 err = dao.SysRole.Ctx(ctx).Where(g.Map{ @@ -98,6 +126,16 @@ func (s *sSysRole) Edit(ctx context.Context, input *model.EditRoleInput) (err er if role == nil { return gerror.New("ID错误,无法修改") } + //判断上级角色是否可以选择 + if input.ParentId != -1 { + var parentRole *entity.SysRole + parentRole, err = s.GetInfoById(ctx, uint(input.ParentId)) + if parentRole == nil { + err = gerror.Newf("无权限选择当前角色") + return + } + } + //查看角色名称是否存在 var roleByName *entity.SysRole err = dao.SysRole.Ctx(ctx).Where(g.Map{ @@ -114,7 +152,7 @@ func (s *sSysRole) Edit(ctx context.Context, input *model.EditRoleInput) (err er role.ListOrder = input.ListOrder role.Remark = input.Remark role.Status = input.Status - role.UpdateBy = uint(loginUserId) + role.UpdatedBy = uint(loginUserId) _, err = dao.SysRole.Ctx(ctx).Data(role).Where(dao.SysRole.Columns().Id, input.Id).Update() if err != nil { return err @@ -124,7 +162,9 @@ func (s *sSysRole) Edit(ctx context.Context, input *model.EditRoleInput) (err er // GetInfoById 根据ID获取角色信息 func (s *sSysRole) GetInfoById(ctx context.Context, id uint) (entity *entity.SysRole, err error) { - err = dao.SysRole.Ctx(ctx).Where(g.Map{ + m := dao.SysRole.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysRole.Columns().Id: id, }).Scan(&entity) return @@ -150,11 +190,13 @@ func (s *sSysRole) DelInfoById(ctx context.Context, id uint) (err error) { if num > 0 { return gerror.New("请先删除子节点!") } + loginUserId := service.Context().GetUserId(ctx) - //开启十五 - err = dao.SysRole.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + //开启事务 + err = dao.SysRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { _, err = dao.SysRole.Ctx(ctx).Data(g.Map{ dao.SysRole.Columns().DeletedBy: uint(loginUserId), + dao.SysRole.Columns().DeletedAt: gtime.Now(), dao.SysRole.Columns().IsDeleted: 1, }).Where(dao.SysRole.Columns().Id, id).Update() //删除角色信息 @@ -176,7 +218,9 @@ func (s *sSysRole) DelInfoById(ctx context.Context, id uint) (err error) { _, err = dao.SysAuthorize.Ctx(ctx).Data(g.Map{ dao.SysAuthorize.Columns().IsDeleted: 1, dao.SysAuthorize.Columns().DeletedBy: loginUserId, + dao.SysAuthorize.Columns().DeletedAt: gtime.Now(), }).Where(dao.SysAuthorize.Columns().RoleId, id).Update() + //删除权限配置 _, err = dao.SysAuthorize.Ctx(ctx).Where(dao.SysAuthorize.Columns().RoleId, id).Delete() return }) @@ -185,33 +229,34 @@ func (s *sSysRole) DelInfoById(ctx context.Context, id uint) (err error) { } // GetRoleList 获取角色列表 -func (s *sSysRole) GetRoleList(ctx context.Context) (list []*model.RoleInfoRes, err error) { - cache := common.Cache() +func (s *sSysRole) GetRoleList(ctx context.Context) (list []*model.RoleInfoOut, err error) { //从缓存获取 - iList := cache.GetOrSetFuncLock(ctx, consts.CacheSysRole, s.getRoleListFromDb, 0, consts.CacheSysAuthTag) + iList, err := cache.Instance().GetOrSetFuncLock(ctx, consts.CacheSysRole, s.getRoleListFromDb, 0) if iList != nil { - err = gconv.Struct(iList, &list) + err = gconv.Struct(iList.Val(), &list) } return } // 从数据库获取所有角色 func (s *sSysRole) getRoleListFromDb(ctx context.Context) (value interface{}, err error) { - err = g.Try(ctx, func(ctx2 context.Context) { - var v []*entity.SysRole - //从数据库获取 - err = dao.SysRole.Ctx(ctx). - Order(dao.SysRole.Columns().ListOrder + " asc," + dao.SysRole.Columns().Id + " asc"). - Scan(&v) - liberr.ErrIsNil(ctx, err, "获取角色数据失败") - value = v - }) + var v []*entity.SysRole + m := dao.SysRole.Ctx(ctx) + //从数据库获取 + err = m. + Order(dao.SysRole.Columns().ListOrder + " asc," + dao.SysRole.Columns().Id + " asc"). + Scan(&v) + if err != nil { + return nil, errors.New("获取角色数据失败") + } + value = v return } // GetInfoByIds 根据ID数组获取角色信息 func (s *sSysRole) GetInfoByIds(ctx context.Context, id []int) (entity []*entity.SysRole, err error) { - err = dao.SysRole.Ctx(ctx).WhereIn(dao.SysRole.Columns().Id, id).Where(g.Map{ + m := dao.SysRole.Ctx(ctx) + err = m.WhereIn(dao.SysRole.Columns().Id, id).Where(g.Map{ dao.SysRole.Columns().Status: 1, dao.SysRole.Columns().IsDeleted: 0, }).Scan(&entity) @@ -235,6 +280,7 @@ func (s *sSysRole) DataScope(ctx context.Context, id int, dataScope uint, deptId if role != nil && role.Status == 0 { return gerror.New("角色已禁用,无法授权") } + //获取登录用户ID loginUserId := service.Context().GetUserId(ctx) @@ -242,7 +288,7 @@ func (s *sSysRole) DataScope(ctx context.Context, id int, dataScope uint, deptId err = g.Try(ctx, func(ctx context.Context) { //修改角色数据范围 role.DataScope = dataScope - role.UpdateBy = uint(loginUserId) + role.UpdatedBy = uint(loginUserId) _, editErr := dao.SysRole.Ctx(ctx).Data(role).Where(dao.SysRole.Columns().Id, id).Update() if editErr != nil { err = gerror.New("修改角色信息失败") @@ -306,6 +352,7 @@ func (s *sSysRole) GetAuthorizeById(ctx context.Context, id int) (menuIds []stri err = gerror.New("角色已禁用,无法查询") return } + //根据角色ID获取权限信息 authorizeInfo, err := service.SysAuthorize().GetInfoByRoleId(ctx, id) if err != nil { diff --git a/internal/logic/system/sys_role_dept.go b/internal/logic/system/sys_role_dept.go index 7f0a546..d9e5e28 100644 --- a/internal/logic/system/sys_role_dept.go +++ b/internal/logic/system/sys_role_dept.go @@ -3,9 +3,9 @@ package system import ( "context" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/dao" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" ) type sSysRoleDept struct { @@ -19,7 +19,7 @@ func sysRoleDeptNew() *sSysRoleDept { return &sSysRoleDept{} } -//GetInfoByRoleId 根据角色ID获取信息 +// GetInfoByRoleId 根据角色ID获取信息 func (s *sSysRoleDept) GetInfoByRoleId(ctx context.Context, roleId int) (data []*entity.SysRoleDept, err error) { var roleDepts []*entity.SysRoleDept err = dao.SysRoleDept.Ctx(ctx).Where(dao.SysRoleDept.Columns().RoleId, roleId).Scan(&roleDepts) diff --git a/internal/logic/system/sys_token.go b/internal/logic/system/sys_token.go index 1dc5898..72037db 100644 --- a/internal/logic/system/sys_token.go +++ b/internal/logic/system/sys_token.go @@ -5,10 +5,14 @@ import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/os/gctx" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/tiger1103/gfast-token/gftoken" + "github.com/gogf/gf/v2/os/glog" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/gftoken" + "strconv" + "strings" "sync" ) @@ -44,12 +48,54 @@ func (m *sSysToken) ParseToken(r *ghttp.Request) (*gftoken.CustomClaims, error) func GfToken() *gftoken.GfToken { ctx := gctx.New() - err := g.Cfg().MustGet(ctx, "gfToken").Struct(&gftService.options) + + //判断控制是否生效 + configDataByIsSecurityControlEnabled, err := service.ConfigData().GetConfigByKey(ctx, consts.SysIsSecurityControlEnabled) + if err != nil { + panic(err.Error()) + } + var configDataByTokenExpiryDate *entity.SysConfig + var configDataBySingleLogin *entity.SysConfig + if configDataByIsSecurityControlEnabled != nil && strings.EqualFold(configDataByIsSecurityControlEnabled.ConfigValue, "1") { + //获取token过期时间 + configDataByTokenExpiryDate, err = service.ConfigData().GetConfigByKey(ctx, consts.SysTokenExpiryDate) + if err != nil { + panic(err.Error()) + } + + //获取是否单一登录系统参数 + configDataBySingleLogin, err = service.ConfigData().GetConfigByKey(ctx, consts.SysIsSingleLogin) + if err != nil { + panic(err.Error()) + } + } + + err = g.Cfg().MustGet(ctx, "gfToken").Struct(&gftService.options) if err != nil { panic(err.Error()) } - prefix := g.Cfg().MustGet(ctx, "system.cache.prefix").String() - m := g.Cfg().MustGet(ctx, "system.cache.model").String() + + //设置token过期时间 + if configDataByTokenExpiryDate != nil { + // 将字符串转换为整数 + var minutes int + minutes, err = strconv.Atoi(configDataByTokenExpiryDate.ConfigValue) + if err != nil { + glog.Debugf(ctx, "无法将字符串转换为整数:%s", err) + panic(err.Error()) + } + gftService.options.Timeout = int64(minutes * 60) + } + //是否开启单一登录 + if configDataBySingleLogin != nil { + if strings.EqualFold(configDataBySingleLogin.ConfigValue, "0") { + gftService.options.MultiLogin = true //允许一个账户多设备登录 + } else if strings.EqualFold(configDataBySingleLogin.ConfigValue, "1") { + gftService.options.MultiLogin = false //禁止一个账户多设备登录 + } + } + prefix := g.Cfg().MustGet(ctx, "cache.prefix").String() + m := g.Cfg().MustGet(ctx, "cache.adapter").String() return GfTokenOption(gftService.options, prefix, m) } diff --git a/internal/logic/system/sys_user.go b/internal/logic/system/sys_user.go index 5422f3b..735d123 100644 --- a/internal/logic/system/sys_user.go +++ b/internal/logic/system/sys_user.go @@ -3,6 +3,7 @@ package system import ( "context" "encoding/json" + "errors" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" @@ -10,14 +11,15 @@ import ( "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gconv" "github.com/gogf/gf/v2/util/grand" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/utility/liberr" - "github.com/sagoo-cloud/sagooiot/utility/utils" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/utility/utils" + "strconv" "strings" "time" ) @@ -61,21 +63,56 @@ func (s *sSysUser) GetAdminUserByUsernamePassword(ctx context.Context, userName } //验证密码是否正确 if utils.EncryptPassword(password, user.UserSalt) != user.UserPassword { + //判断获取是否启动安全控制和允许再次登录时间 + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysAgainLoginDate} + var configDatas []*entity.SysConfig + configDatas, err = service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + isSecurityControlEnabled := "0" //是否启动安全控制 + againLoginDate := 1 //允许再次登录时间 + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysIsSecurityControlEnabled) { + isSecurityControlEnabled = configData.ConfigValue + } + if strings.EqualFold(configData.ConfigKey, consts.SysAgainLoginDate) { + againLoginDate, err = strconv.Atoi(configData.ConfigValue) + if err != nil { + err = gerror.New("允许再次登录时间配置错误") + return nil, err + } + } + } + if strings.EqualFold(isSecurityControlEnabled, "1") { + tmpData, _ := cache.Instance().Get(ctx, consts.CacheSysErrorPrefix+"_"+gconv.String(userName)) + var num = 1 + if tmpData.Val() != nil { + tempValue, _ := strconv.Atoi(tmpData.Val().(string)) + num = tempValue + 1 + } + //存入缓存 + err := cache.Instance().Set(ctx, consts.CacheSysErrorPrefix+"_"+gconv.String(userName), num, time.Duration(againLoginDate*60)*time.Second) + if err != nil { + return nil, err + } + } err = gerror.New("密码错误") return } - return user, nil + return } // UpdateLoginInfo 更新用户登录信息 func (s *sSysUser) UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error) { - err = g.Try(ctx, func(ctx context.Context) { - _, err = dao.SysUser.Ctx(ctx).WherePri(id).Update(g.Map{ - dao.SysUser.Columns().LastLoginIp: ip, - dao.SysUser.Columns().LastLoginTime: gtime.Now(), - }) - liberr.ErrIsNil(ctx, err, "更新用户登录信息失败") + _, err = dao.SysUser.Ctx(ctx).WherePri(id).Update(g.Map{ + dao.SysUser.Columns().LastLoginIp: ip, + dao.SysUser.Columns().LastLoginTime: gtime.Now(), }) + if err != nil { + return errors.New("更新用户登录信息失败") + } + return } @@ -89,7 +126,7 @@ func (s *sSysUser) UserList(ctx context.Context, input *model.UserListDoInput) ( if input.DeptId != 0 { //m = m.Where(dao.SysUser.Columns().DeptId, req.DeptId) deptIds, _ := s.getSearchDeptIds(ctx, gconv.Int64(input.DeptId)) - m = m.Where("dept_id in (?)", deptIds) + m = m.WhereIn(dao.SysUser.Columns().DeptId, deptIds) } if input.UserName != "" { m = m.Where(dao.SysUser.Columns().UserName, input.UserName) @@ -104,6 +141,7 @@ func (s *sSysUser) UserList(ctx context.Context, input *model.UserListDoInput) ( m = m.Where("created_at >=? AND created_at 0 { return gerror.New("手机号已存在") } + + //判断是否有权限选择当前部门 + _, err = service.SysDept().Detail(ctx, int64(input.DeptId)) + if err != nil { + return + } + //开启事务管理 - err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { //添加用户 var sysUser *entity.SysUser if err = gconv.Scan(input, &sysUser); err != nil { @@ -203,13 +248,43 @@ func (s *sSysUser) Add(ctx context.Context, input *model.AddUserInput) (err erro sysUser.IsDeleted = 0 //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) - sysUser.CreateBy = uint(loginUserId) //添加用户信息 - lastInsertId, err := dao.SysUser.Ctx(ctx).Data(sysUser).InsertAndGetId() + result, err := dao.SysUser.Ctx(ctx).Data(do.SysUser{ + UserName: sysUser.UserName, + UserTypes: sysUser.UserTypes, + Mobile: sysUser.Mobile, + UserNickname: sysUser.UserNickname, + Birthday: sysUser.Birthday, + UserPassword: sysUser.UserPassword, + UserSalt: sysUser.UserSalt, + UserEmail: sysUser.UserEmail, + Sex: sysUser.Sex, + Avatar: sysUser.Avatar, + DeptId: sysUser.DeptId, + Remark: sysUser.Remark, + IsAdmin: sysUser.IsAdmin, + Address: sysUser.Address, + Describe: sysUser.Describe, + Status: sysUser.Status, + IsDeleted: sysUser.IsDeleted, + CreatedBy: uint(loginUserId), + CreatedAt: gtime.Now(), + }).Insert() if err != nil { return gerror.New("添加用户失败") } - err = BindUserAndPost(ctx, int(lastInsertId), input.PostIds) + + //获取主键ID + lastInsertId, err := service.Sequences().GetSequences(ctx, result, dao.SysUser.Table(), dao.SysUser.Columns().Id) + if err != nil { + return + } + + //绑定岗位 + err = service.SysUserPost().BindUserAndPost(ctx, int(lastInsertId), input.PostIds) + + //绑定角色 + err = service.SysUserRole().BindUserAndRole(ctx, int(lastInsertId), input.RoleIds) return err }) @@ -245,8 +320,9 @@ func (s *sSysUser) Edit(ctx context.Context, input *model.EditUserInput) (err er if sysUserByMobile != nil && sysUserByMobile.Id != input.Id { return gerror.New("手机号已存在") } + //开启事务管理 - err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) (err error) { + err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { //编辑用户 sysUser.UserNickname = input.UserNickname sysUser.DeptId = input.DeptId @@ -268,86 +344,23 @@ func (s *sSysUser) Edit(ctx context.Context, input *model.EditUserInput) (err er if err != nil { return gerror.New("编辑用户失败") } - //删除原有用户与岗位绑定管理 - _, err = dao.SysUserPost.Ctx(ctx).Where(dao.SysUserPost.Columns().UserId, input.Id).Delete() - if err != nil { - return gerror.New("删除用户与岗位绑定关系失败") - } - - err = BindUserAndPost(ctx, int(input.Id), input.PostIds) - //删除原有用户与角色绑定管理 - _, err = dao.SysUserRole.Ctx(ctx).Where(dao.SysUserRole.Columns().UserId, input.Id).Delete() - if err != nil { - return gerror.New("删除用户与角色绑定关系失败") - } + //绑定岗位 + err = service.SysUserPost().BindUserAndPost(ctx, int(input.Id), input.PostIds) - err = BindUserAndRole(ctx, int(input.Id), input.RoleIds) + //绑定角色 + err = service.SysUserRole().BindUserAndRole(ctx, int(input.Id), input.RoleIds) return err }) return } -// BindUserAndPost 添加用户与岗位绑定关系 -func BindUserAndPost(ctx context.Context, userId int, postIds []int) (err error) { - if len(postIds) > 0 { - var sysUserPosts []*entity.SysUserPost - //查询用户与岗位是否存在 - for _, postId := range postIds { - var sysUserPost *entity.SysUserPost - err = dao.SysUserPost.Ctx(ctx).Where(g.Map{ - dao.SysUserPost.Columns().UserId: userId, - dao.SysUserPost.Columns().PostId: postId, - }).Scan(&sysUserPost) - - if sysUserPost == nil { - //添加用户与岗位绑定管理 - sysUserPost = new(entity.SysUserPost) - sysUserPost.UserId = userId - sysUserPost.PostId = postId - sysUserPosts = append(sysUserPosts, sysUserPost) - } - } - _, err = dao.SysUserPost.Ctx(ctx).Data(sysUserPosts).Insert() - if err != nil { - return gerror.New("绑定岗位失败") - } - } - return -} - -// BindUserAndRole 添加用户与角色绑定关系 -func BindUserAndRole(ctx context.Context, userId int, roleIds []int) (err error) { - if len(roleIds) > 0 { - var sysUserRoles []*entity.SysUserRole - //查询用户与角色是否存在 - for _, roleId := range roleIds { - var sysUserRole *entity.SysUserRole - err = dao.SysUserRole.Ctx(ctx).Where(g.Map{ - dao.SysUserRole.Columns().UserId: userId, - dao.SysUserRole.Columns().RoleId: roleId, - }).Scan(&sysUserRole) - - if sysUserRole == nil { - //添加用户与角色绑定管理 - sysUserRole = new(entity.SysUserRole) - sysUserRole.UserId = userId - sysUserRole.RoleId = roleId - sysUserRoles = append(sysUserRoles, sysUserRole) - } - } - _, err = dao.SysUserRole.Ctx(ctx).Data(sysUserRoles).Insert() - if err != nil { - return gerror.New("绑定角色失败") - } - } - return -} - // GetUserById 根据ID获取用户信息 func (s *sSysUser) GetUserById(ctx context.Context, id uint) (out *model.UserInfoOut, err error) { var e *entity.SysUser - err = dao.SysUser.Ctx(ctx).Where(g.Map{ + m := dao.SysUser.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysUser.Columns().Id: id, dao.SysUser.Columns().IsDeleted: 0, }).Scan(&e) @@ -401,18 +414,16 @@ func (s *sSysUser) DelInfoById(ctx context.Context, id uint) (err error) { if sysUser.IsDeleted == 1 { return gerror.New("用户已删除,无须重复删除") } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) _, err = dao.SysUser.Ctx(ctx).Data(g.Map{ dao.SysUser.Columns().IsDeleted: 1, dao.SysUser.Columns().DeletedBy: loginUserId, + dao.SysUser.Columns().DeletedAt: gtime.Now(), }).Where(g.Map{ dao.SysUser.Columns().Id: id, }).Update() - //删除用户 - _, err = dao.SysUser.Ctx(ctx).Where(g.Map{ - dao.SysUser.Columns().Id: id, - }).Delete() if err != nil { return gerror.New("删除用户失败") } @@ -436,6 +447,66 @@ func (s *sSysUser) ResetPassword(ctx context.Context, id uint, userPassword stri return gerror.New("用户已删除,无法重置密码") } + //判断是否启用了安全控制和启用了RSA + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysIsRsaEnabled} + configDatas, err := service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + var isSecurityControlEnabled = "0" //是否启用安装控制 + var isRsaEnbled = "0" //是否启用RSA + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysIsSecurityControlEnabled) { + isSecurityControlEnabled = configData.ConfigValue + } + if strings.EqualFold(configData.ConfigKey, consts.SysIsRsaEnabled) { + isRsaEnbled = configData.ConfigValue + } + } + + if strings.EqualFold(isSecurityControlEnabled, "1") { + if strings.EqualFold(isRsaEnbled, "1") { + //对用户密码进行解密 + userPassword, err = utils.Decrypt(consts.RsaPrivateKeyFile, userPassword, consts.RsaOAEP) + if err != nil { + return + } + } + + //校验密码 + err = s.CheckPassword(ctx, userPassword) + if err != nil { + return + } + + //获取用户修改密码的历史记录 + var history []*entity.SysUserPasswordHistory + if err = dao.SysUserPasswordHistory.Ctx(ctx).Where(dao.SysUserPasswordHistory.Columns().UserId, id).OrderDesc(dao.SysUserPasswordHistory.Columns().CreatedAt).Scan(&history); err != nil { + return + } + + if history != nil { + //判断与最近三次是否一样 + var num int + if len(history) > 3 { + num = 3 + } else { + num = len(history) + } + var isExit = false + for i := 0; i < num; i++ { + if strings.EqualFold(history[i].AfterPassword, utils.EncryptPassword(userPassword, sysUser.UserSalt)) { + isExit = true + break + } + } + if isExit { + err = gerror.New("密码与前三次重复,请重新输入!") + return + } + } + } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) //判断当前登录用户是否为超级管理员角色 @@ -453,11 +524,31 @@ func (s *sSysUser) ResetPassword(ctx context.Context, id uint, userPassword stri } sysUser.UserSalt = grand.S(10) - sysUser.UserPassword = utils.EncryptPassword(userPassword, sysUser.UserSalt) + + beforePassword := sysUser.UserPassword + afterPassword := utils.EncryptPassword(userPassword, sysUser.UserSalt) + sysUser.UserPassword = afterPassword sysUser.UpdatedBy = uint(loginUserId) - _, err = dao.SysUser.Ctx(ctx).Data(sysUser).Where(g.Map{ - dao.SysUser.Columns().Id: id, - }).Update() + + //开启事务管理 + err = dao.SysUser.Transaction(ctx, func(ctx context.Context, tx gdb.TX) (err error) { + _, err = dao.SysUser.Ctx(ctx).Data(sysUser).Where(g.Map{ + dao.SysUser.Columns().Id: id, + }).Update() + + //添加 + _, err = dao.SysUserPasswordHistory.Ctx(ctx).Data(&do.SysUserPasswordHistory{ + UserId: sysUser.Id, + BeforePassword: beforePassword, + AfterPassword: afterPassword, + ChangeTime: gtime.Now(), + CreatedAt: gtime.Now(), + CreatedBy: loginUserId, + }).Insert() + + return + }) + if err != nil { return gerror.New("重置密码失败") } @@ -477,6 +568,7 @@ func (s *sSysUser) EditUserStatus(ctx context.Context, id uint, status uint) (er if sysUser.Status == status { return gerror.New("无须重复修改状态") } + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) sysUser.Status = status @@ -493,8 +585,10 @@ func (s *sSysUser) EditUserStatus(ctx context.Context, id uint, status uint) (er // 获取搜索的部门ID数组 func (s *sSysUser) getSearchDeptIds(ctx context.Context, deptId int64) (deptIds []int64, err error) { err = g.Try(ctx, func(ctx context.Context) { - deptAll, e := sysDeptNew().GetFromCache(ctx) - liberr.ErrIsNil(ctx, e) + deptAll, err := sysDeptNew().GetFromCache(ctx) + if err != nil { + return + } deptWithChildren := sysDeptNew().FindSonByParentId(deptAll, gconv.Int64(deptId)) deptIds = make([]int64, len(deptWithChildren)) for k, v := range deptWithChildren { @@ -517,7 +611,9 @@ func GetDeptNameDict(ctx context.Context, ids g.Slice) (dict map[int64]model.Det // GetUserByIds 根据ID数据获取用户信息 func (s *sSysUser) GetUserByIds(ctx context.Context, id []int) (data []*entity.SysUser, err error) { - err = dao.SysUser.Ctx(ctx).Where(g.Map{ + m := dao.SysUser.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysUser.Columns().IsDeleted: 0, }).WhereIn(dao.SysUser.Columns().Id, id).Scan(&data) return @@ -525,26 +621,28 @@ func (s *sSysUser) GetUserByIds(ctx context.Context, id []int) (data []*entity.S // GetAll 获取所有用户信息 func (s *sSysUser) GetAll(ctx context.Context) (data []*entity.SysUser, err error) { - err = dao.SysUser.Ctx(ctx).Where(g.Map{ + m := dao.SysUser.Ctx(ctx) + + err = m.Where(g.Map{ dao.SysUser.Columns().IsDeleted: 0, }).Scan(&data) return } func (s *sSysUser) CurrentUser(ctx context.Context) (userInfoOut *model.UserInfoOut, menuTreeOut []*model.UserMenuTreeOut, err error) { - cache := common.Cache() - //获取当前登录用户信息 loginUserId := service.Context().GetUserId(ctx) if loginUserId == 0 { err = gerror.New("无登录用户信息,请先登录!") return } - tmpUserAuthorize := cache.Get(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId)) - tmpUserInfo := cache.Get(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId)) + tmpUserAuthorize, err := cache.Instance().Get(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId)) + tmpUserInfo, err := cache.Instance().Get(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId)) if tmpUserAuthorize.Val() != nil && tmpUserInfo.Val() != nil { - json.Unmarshal([]byte(tmpUserAuthorize.Val().(string)), &menuTreeOut) - json.Unmarshal([]byte(tmpUserInfo.Val().(string)), &userInfoOut) + if err = json.Unmarshal([]byte(tmpUserAuthorize.Val().(string)), &menuTreeOut); err != nil { + return + } + err = json.Unmarshal([]byte(tmpUserInfo.Val().(string)), &userInfoOut) return } @@ -554,7 +652,10 @@ func (s *sSysUser) CurrentUser(ctx context.Context) (userInfoOut *model.UserInfo return } if userInfo != nil { - cache.Set(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId), userInfo, time.Hour) + err := cache.Instance().Set(ctx, consts.CacheUserInfo+"_"+gconv.String(loginUserId), userInfo, time.Hour) + if err != nil { + return nil, nil, err + } } //根据当前登录用户ID查询用户角色信息 @@ -608,7 +709,10 @@ func (s *sSysUser) CurrentUser(ctx context.Context) (userInfoOut *model.UserInfo menuTreeOut = GetUserMenuTree(userMenuTreeOut) if menuTreeOut != nil { - cache.Set(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId), menuTreeOut, time.Hour) + err := cache.Instance().Set(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId), menuTreeOut, 0) + if err != nil { + return nil, nil, err + } } return } else { @@ -622,25 +726,98 @@ func (s *sSysUser) CurrentUser(ctx context.Context) (userInfoOut *model.UserInfo err = gerror.New("无权限配置,请联系管理员") return } + + isSecurityControlEnabled := 0 //是否启用安全控制 + sysColumnSwitch := 0 //列表开关 + sysButtonSwitch := 0 //按钮开关 + sysApiSwitch := 0 //api开关 + //判断是否启用了安全控制、列表开关、按钮开关、api开关 + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysColumnSwitch, consts.SysButtonSwitch, consts.SysApiSwitch} + var configDatas []*entity.SysConfig + configDatas, err = service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysIsSecurityControlEnabled) { + isSecurityControlEnabled = gconv.Int(configData.ConfigValue) + } + if strings.EqualFold(configData.ConfigKey, consts.SysColumnSwitch) { + sysColumnSwitch = gconv.Int(configData.ConfigValue) + } + if strings.EqualFold(configData.ConfigKey, consts.SysButtonSwitch) { + sysButtonSwitch = gconv.Int(configData.ConfigValue) + } + if strings.EqualFold(configData.ConfigKey, consts.SysApiSwitch) { + sysApiSwitch = gconv.Int(configData.ConfigValue) + } + } //菜单Ids var menuIds []int - //按钮Ids var menuButtonIds []int //列表Ids var menuColumnIds []int //API Ids var menuApiIds []int + for _, authorize := range authorizeInfo { if strings.EqualFold(authorize.ItemsType, consts.Menu) { menuIds = append(menuIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Button) { + } else if strings.EqualFold(authorize.ItemsType, consts.Button) && sysButtonSwitch == 1 { menuButtonIds = append(menuButtonIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Column) { + } else if strings.EqualFold(authorize.ItemsType, consts.Column) && sysColumnSwitch == 1 { menuColumnIds = append(menuColumnIds, authorize.ItemsId) - } else if strings.EqualFold(authorize.ItemsType, consts.Api) { + } else if strings.EqualFold(authorize.ItemsType, consts.Api) && sysApiSwitch == 1 { menuApiIds = append(menuApiIds, authorize.ItemsId) } } + //判断按钮、列表、API开关状态,如果关闭则获取菜单对应的所有信息 + //判断按钮开关 + if isSecurityControlEnabled == 0 { + if sysButtonSwitch == 0 { + //获取所有按钮 + var menuButtons []*entity.SysMenuButton + menuButtons, err = service.SysMenuButton().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuButtons) > 0 { + for _, menuButton := range menuButtons { + menuButtonIds = append(menuButtonIds, int(menuButton.Id)) + } + } + } + + //判断列表开关 + if sysColumnSwitch == 0 { + //获取所有列表 + var menuColumns []*entity.SysMenuColumn + menuColumns, err = service.SysMenuColumn().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuColumns) > 0 { + for _, menuColumn := range menuColumns { + menuColumnIds = append(menuColumnIds, int(menuColumn.Id)) + } + } + } + //判断API开关 + if sysApiSwitch == 0 { + //获取所有API + var menuApis []*entity.SysMenuApi + menuApis, err = service.SysMenuApi().GetInfoByMenuIds(ctx, menuIds) + if err != nil { + return + } + if len(menuApis) > 0 { + for _, menuApi := range menuApis { + menuApiIds = append(menuApiIds, int(menuApi.Id)) + } + } + } + } + //获取所有菜单信息 var menuInfo []*entity.SysMenu menuInfo, err = service.SysMenu().GetInfoByMenuIds(ctx, menuIds) @@ -719,7 +896,10 @@ func (s *sSysUser) CurrentUser(ctx context.Context) (userInfoOut *model.UserInfo //对菜单进行树状重组 menuTreeOut = GetUserMenuTree(userMenuTreeOut) if menuTreeOut != nil { - cache.Set(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId), menuTreeOut, time.Hour) + err := cache.Instance().Set(ctx, consts.CacheUserAuthorize+"_"+gconv.String(loginUserId), menuTreeOut, 0) + if err != nil { + return nil, nil, err + } } return } @@ -736,9 +916,18 @@ func (s *sSysUser) EditUserAvatar(ctx context.Context, id uint, avatar string) ( return } sysUser.Avatar = avatar + //获取当前登录用户ID loginUserId := service.Context().GetUserId(ctx) + + //判断是否为当前用户 + if id != uint(loginUserId) { + err = gerror.New("无法修改其他用户头像") + return + } + sysUser.UpdatedBy = uint(loginUserId) + sysUser.UpdatedAt = gtime.Now() _, err = dao.SysUser.Ctx(ctx).Data(sysUser).Where(g.Map{ dao.SysUser.Columns().Id: id, }).Update() @@ -789,3 +978,93 @@ func (s *sSysUser) EditUserInfo(ctx context.Context, input *model.EditUserInfoIn } return } + +// CheckPassword 校验用户密码 +func (s *sSysUser) CheckPassword(ctx context.Context, userPassword string) (err error) { + keys := []string{consts.SysPasswordMinimumLength, consts.SysRequireComplexity, consts.SysRequireDigit, consts.SysRequireLowercaseLetter, consts.SysRequireUppercaseLetter} + var configData []*entity.SysConfig + configData, err = service.ConfigData().GetByKeys(ctx, keys) + if err != nil { + return + } + if configData == nil || len(configData) == 0 { + err = gerror.New(g.I18n().T(ctx, "{#sysUserPwCheckConfig}")) + } + var minimumLength int + var complexity int + var digit int + var lowercaseLetter int + var uppercaseLetter int + for _, data := range configData { + if strings.EqualFold(data.ConfigKey, consts.SysPasswordMinimumLength) { + minimumLength, _ = strconv.Atoi(data.ConfigValue) + } + if strings.EqualFold(data.ConfigKey, consts.SysRequireComplexity) { + complexity, _ = strconv.Atoi(data.ConfigValue) + } + if strings.EqualFold(data.ConfigKey, consts.SysRequireDigit) { + digit, _ = strconv.Atoi(data.ConfigValue) + } + if strings.EqualFold(data.ConfigKey, consts.SysRequireLowercaseLetter) { + lowercaseLetter, _ = strconv.Atoi(data.ConfigValue) + } + if strings.EqualFold(data.ConfigKey, consts.SysRequireUppercaseLetter) { + uppercaseLetter, _ = strconv.Atoi(data.ConfigValue) + } + } + + var flag bool + flag, err = utils.ValidatePassword(userPassword, minimumLength, complexity, digit, lowercaseLetter, uppercaseLetter) + if err != nil { + return + } + if !flag { + err = gerror.New(g.I18n().T(ctx, "{#sysUserPwCheckError}")) + return + } + return +} + +// EditPassword 修改密码 +func (s *sSysUser) EditPassword(ctx context.Context, userName string, oldUserPassword string, userPassword string) (err error) { + //判断是否启用了安全控制和启用了RSA + configKeys := []string{consts.SysIsSecurityControlEnabled, consts.SysIsRsaEnabled} + configDatas, err := service.ConfigData().GetByKeys(ctx, configKeys) + if err != nil { + return + } + var isSecurityControlEnabled = "0" + var isRSAEnbled = "0" + for _, configData := range configDatas { + if strings.EqualFold(configData.ConfigKey, consts.SysIsSecurityControlEnabled) { + isSecurityControlEnabled = configData.ConfigValue + } + if strings.EqualFold(configData.ConfigKey, consts.SysIsRsaEnabled) { + isRSAEnbled = configData.ConfigValue + } + } + if strings.EqualFold(isSecurityControlEnabled, "1") && strings.EqualFold(isRSAEnbled, "1") { + //对账号进行解密 + oldUserPassword, err = utils.Decrypt(consts.RsaPrivateKeyFile, oldUserPassword, consts.RsaOAEP) + if err != nil { + return + } + } + + //获取用户信息 + userInfo, err := service.SysUser().GetUserByUsername(ctx, userName) + if err != nil { + return + } + //判断旧密码是否一致 + if !strings.EqualFold(userInfo.UserPassword, utils.EncryptPassword(oldUserPassword, userInfo.UserSalt)) { + err = gerror.New("原密码输入错误, 请重新输入!") + return + } + //重置密码 + err = s.ResetPassword(ctx, uint(userInfo.Id), userPassword) + if err != nil { + return + } + return +} diff --git a/internal/logic/system/sys_user_online.go b/internal/logic/system/sys_user_online.go index 37156f1..bd3b992 100644 --- a/internal/logic/system/sys_user_online.go +++ b/internal/logic/system/sys_user_online.go @@ -5,12 +5,13 @@ import ( "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/grpool" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/logic/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/consts" + "sagooiot/internal/dao" + "sagooiot/internal/model" + "sagooiot/internal/model/do" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/cache" ) type sSysUserOnline struct { @@ -37,7 +38,16 @@ func (s *sSysUserOnline) Invoke(ctx context.Context, data *entity.SysUserOnline) // Add 记录用户在线 func (s *sSysUserOnline) Add(ctx context.Context, data *entity.SysUserOnline) { - _, err := dao.SysUserOnline.Ctx(ctx).Data(data).Save() + _, err := dao.SysUserOnline.Ctx(ctx).Data(do.SysUserOnline{ + Uuid: data.Uuid, + Key: data.Key, + Token: data.Token, + CreatedAt: data.CreatedAt, + UserName: data.UserName, + Ip: data.Ip, + Explorer: data.Explorer, + Os: data.Os, + }).Insert() if err != nil { g.Log().Error(ctx, err) } @@ -59,7 +69,7 @@ func (s *sSysUserOnline) GetInfoByToken(ctx context.Context, token string) (data } // DelByIds 根据IDS删除信息 -func (s *sSysUserOnline) DelByIds(ctx context.Context, ids []uint) (err error) { +func (s *sSysUserOnline) DelByIds(ctx context.Context, ids []int) (err error) { _, err = dao.SysUserOnline.Ctx(ctx).WhereIn(dao.SysUserOnline.Columns().Id, ids).Delete() if err != nil { return gerror.New("删除失败") @@ -85,7 +95,7 @@ func (s *sSysUserOnline) UserOnlineList(ctx context.Context, input *model.UserOn input.PageNum = 1 } if input.PageSize == 0 { - input.PageSize = consts.DefaultPageSize + input.PageSize = consts.PageSize } //获取在线用户信息 err = m.Page(input.PageNum, input.PageSize).OrderDesc(dao.SysUser.Columns().CreatedAt).Scan(&out) @@ -103,7 +113,7 @@ func (s *sSysUserOnline) UserOnlineStrongBack(ctx context.Context, id int) (err return gerror.New("ID错误") } //删除缓存信息 - common.Cache().Remove(ctx, userOnline.Key) + _, err = cache.Instance().Remove(ctx, userOnline.Key) //删除在线用户 _, err = dao.SysUserOnline.Ctx(ctx).Where(dao.SysUserOnline.Columns().Id, id).Delete() return diff --git a/internal/logic/system/sys_user_post.go b/internal/logic/system/sys_user_post.go index 6b48b4d..6114e9e 100644 --- a/internal/logic/system/sys_user_post.go +++ b/internal/logic/system/sys_user_post.go @@ -2,10 +2,11 @@ package system import ( "context" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/dao" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" ) type sSysUserPost struct { @@ -19,7 +20,7 @@ func sysUserPostNew() *sSysUserPost { return &sSysUserPost{} } -//GetInfoByUserId 根据用户ID获取信息 +// GetInfoByUserId 根据用户ID获取信息 func (s *sSysUserPost) GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserPost, err error) { var userPost []*entity.SysUserPost err = dao.SysUserPost.Ctx(ctx).Where(dao.SysUserPost.Columns().UserId, userId).Scan(&userPost) @@ -39,3 +40,37 @@ func (s *sSysUserPost) GetInfoByUserId(ctx context.Context, userId int) (data [] } return } + +// BindUserAndPost 添加用户与岗位绑定关系 +func (s *sSysUserPost) BindUserAndPost(ctx context.Context, userId int, postIds []int) (err error) { + if len(postIds) > 0 { + //删除原有用户与岗位绑定管理 + _, err = dao.SysUserPost.Ctx(ctx).Where(dao.SysUserPost.Columns().UserId, userId).Delete() + if err != nil { + return gerror.New("删除用户与岗位绑定关系失败") + } + + var sysUserPosts []*entity.SysUserPost + //查询用户与岗位是否存在 + for _, postId := range postIds { + /*var sysUserPost *entity.SysUserPost + err = dao.SysUserPost.Ctx(ctx).Where(g.Map{ + dao.SysUserPost.Columns().UserId: userId, + dao.SysUserPost.Columns().PostId: postId, + }).Scan(&sysUserPost) + if sysUserPost == nil { + + }*/ + //添加用户与岗位绑定管理 + var sysUserPost = new(entity.SysUserPost) + sysUserPost.UserId = userId + sysUserPost.PostId = postId + sysUserPosts = append(sysUserPosts, sysUserPost) + } + _, err = dao.SysUserPost.Ctx(ctx).Data(sysUserPosts).Insert() + if err != nil { + return gerror.New("绑定岗位失败") + } + } + return +} diff --git a/internal/logic/system/sys_user_role.go b/internal/logic/system/sys_user_role.go index 43349a0..d61ac07 100644 --- a/internal/logic/system/sys_user_role.go +++ b/internal/logic/system/sys_user_role.go @@ -2,10 +2,13 @@ package system import ( "context" + "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/dao" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - "github.com/sagoo-cloud/sagooiot/internal/service" + "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/dao" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" ) type sSysUserRole struct { @@ -19,7 +22,7 @@ func sysUserRoleNew() *sSysUserRole { return &sSysUserRole{} } -//GetInfoByUserId 根据用户ID获取信息 +// GetInfoByUserId 根据用户ID获取信息 func (s *sSysUserRole) GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserRole, err error) { var userRole []*entity.SysUserRole err = dao.SysUserRole.Ctx(ctx).Where(dao.SysUserRole.Columns().UserId, userId).Scan(&userRole) @@ -39,3 +42,39 @@ func (s *sSysUserRole) GetInfoByUserId(ctx context.Context, userId int) (data [] } return } + +// BindUserAndRole 添加用户与角色绑定关系 +func (s *sSysUserRole) BindUserAndRole(ctx context.Context, userId int, roleIds []int) (err error) { + if len(roleIds) > 0 { + //删除原有用户与角色绑定管理 + _, err = dao.SysUserRole.Ctx(ctx).Where(dao.SysUserRole.Columns().UserId, userId).Delete() + if err != nil { + return gerror.New("删除用户与角色绑定关系失败") + } + + var sysUserRoles []*entity.SysUserRole + for _, roleId := range roleIds { + /*var sysUserRole *entity.SysUserRole + err = dao.SysUserRole.Ctx(ctx).Where(g.Map{ + dao.SysUserRole.Columns().UserId: userId, + dao.SysUserRole.Columns().RoleId: roleId, + }).Scan(&sysUserRole)*/ + + //添加用户与角色绑定管理 + var sysUserRole = new(entity.SysUserRole) + sysUserRole.UserId = userId + sysUserRole.RoleId = roleId + sysUserRoles = append(sysUserRoles, sysUserRole) + } + _, err = dao.SysUserRole.Ctx(ctx).Data(sysUserRoles).Insert() + if err != nil { + return gerror.New("绑定角色失败") + } + //删除缓存 + _, err = gcache.Remove(ctx, "RoleListAtName"+gconv.String(userId)) + if err != nil { + return err + } + } + return +} diff --git a/internal/logic/tdengine/td_engine.go b/internal/logic/tdengine/td_engine.go index 4945ace..d13a854 100644 --- a/internal/logic/tdengine/td_engine.go +++ b/internal/logic/tdengine/td_engine.go @@ -3,290 +3,144 @@ package tdengine import ( "context" "database/sql" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/service" + "sync" "time" "github.com/gogf/gf/v2/container/gvar" - "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" - _ "github.com/taosdata/driver-go/v3/taosRestful" - //_ "github.com/taosdata/driver-go/v3/taosSql" //原生驱动 ) -type sTdEngine struct { -} +type sTdEngine struct{} + +var _instance *sTdEngine +var once sync.Once +// 使用单例模式创建sTdEngine实例 func tdEngineNew() *sTdEngine { - return &sTdEngine{} + once.Do(func() { + _instance = &sTdEngine{} + }) + return _instance } +var dbName = "sagoo_iot" + +// 初始化函数 func init() { service.RegisterTdEngine(tdEngineNew()) - - name, _ := g.Cfg().Get(context.TODO(), "tdengine.dbName") - if name.String() != "" { - dbName = name.String() - } + // 简化配置获取过程 + dbName = g.Cfg().MustGet(context.Background(), "tdengine.dbName", "sagoo_iot").String() } -// 数据库名 -var dbName = "sagoo_iot" - -// GetConn 获取链接 -func (s *sTdEngine) GetConn(ctx context.Context, dbName string) (db *sql.DB, err error) { - driver, err := g.Cfg().Get(ctx, "tdengine.type") - if err != nil { - err = gerror.New("请检查TDengine配置") - return - } - dsn, err := g.Cfg().Get(ctx, "tdengine.dsn") - if err != nil { - err = gerror.New("请检查TDengine配置") - return - } - - link := dsn.String() - if dbName != "" { - link += dbName - } - - taos, err := sql.Open(driver.String(), link) - if err != nil { - g.Log().Error(ctx, err) - err = gerror.New("TDengine连接失败") - return - } - return taos, nil +type connections struct { + tdEngineType string + tdDsn string + ConnectionMap map[string]*sql.DB + sync.RWMutex } -// GetTdEngineAllDb 获取所有数据库 -func (s *sTdEngine) GetTdEngineAllDb(ctx context.Context) (data []string, err error) { - taos, err := s.GetConn(ctx, "") - if err != nil { - err = gerror.New("获取链接失败") - return - } - - defer taos.Close() +var connectionMap *connections - rows, err := taos.Query("show databases;") - if err != nil { - err = gerror.New(err.Error()) - return - } - defer rows.Close() - - for rows.Next() { - var name string - - err = rows.Scan(&name) - data = append(data, name) - } - return -} - -// GetListTableByDatabases 获取指定数据库下所有的表列表 -func (s *sTdEngine) GetListTableByDatabases(ctx context.Context, dbName string) (data []*model.TDEngineTablesList, err error) { - taos, err := s.GetConn(ctx, dbName) - if err != nil { - err = gerror.New("获取链接失败") - return +// GetConn 获取数据库连接 +func (s *sTdEngine) GetConn(ctx context.Context, dbName string) (*sql.DB, error) { + if connectionMap == nil { + // 一次性初始化connectionMap + connectionMap = initConnectionMap(ctx) + if connectionMap == nil { + return nil, gerror.New("TDengine配置初始化失败") + } } - defer taos.Close() + connectionMap.RLock() + connection, exists := connectionMap.ConnectionMap[dbName] + connectionMap.RUnlock() - rows, err := taos.Query("SELECT table_name AS tableName, db_name AS dbName, create_time AS createTime, stable_name AS stableName FROM information_schema.ins_tables WHERE db_name = '" + dbName + "'") - if err != nil { - err = gerror.New(err.Error()) - return + if !exists { + return createNewConnection(ctx, dbName) } - defer rows.Close() - for rows.Next() { - var tableName, db, stableName string - var createTime *gtime.Time - err = rows.Scan(&tableName, &db, &createTime, &stableName) - if err != nil { - err = gerror.New("获取失败") - return - } - var tDEngineTablesList = new(model.TDEngineTablesList) - tDEngineTablesList.TableName = tableName - tDEngineTablesList.DbName = db - tDEngineTablesList.StableName = stableName - tDEngineTablesList.CreateTime = createTime - data = append(data, tDEngineTablesList) - } - return + return connection, nil } -// GetTdEngineTableInfoByTable 获取指定数据表结构信息 -func (s *sTdEngine) GetTdEngineTableInfoByTable(ctx context.Context, dbName string, tableName string) (data []*model.TDEngineTableInfo, err error) { - taos, err := s.GetConn(ctx, dbName) - if err != nil { - err = gerror.New("获取链接失败") - return +// 初始化连接映射 +func initConnectionMap(ctx context.Context) *connections { + driver := g.Cfg().MustGet(ctx, "tsd.tdengine.type") + dsn := g.Cfg().MustGet(ctx, "tsd.tdengine.dsn") + return &connections{ + tdEngineType: driver.String(), + tdDsn: dsn.String(), + ConnectionMap: make(map[string]*sql.DB), } +} - defer taos.Close() - - rows, err := taos.Query("DESCRIBE " + dbName + "." + tableName + ";") - if err != nil { - err = gerror.New(err.Error()) - return - } - defer rows.Close() +// 创建新连接 +func createNewConnection(ctx context.Context, dbName string) (*sql.DB, error) { + connectionMap.Lock() + defer connectionMap.Unlock() - for rows.Next() { - var tDEngineTableInfo = new(model.TDEngineTableInfo) - err = rows.Scan(&tDEngineTableInfo.Field, &tDEngineTableInfo.Type, &tDEngineTableInfo.Length, &tDEngineTableInfo.Note) - if err != nil { - err = gerror.New("获取失败") - return - } - data = append(data, tDEngineTableInfo) + // 再次检查以防止竞态条件 + if conn, exists := connectionMap.ConnectionMap[dbName]; exists { + return conn, nil } - return -} -// GetTdEngineTableDataByTable 获取指定数据表数据信息 -func (s *sTdEngine) GetTdEngineTableDataByTable(ctx context.Context, dbName string, tableName string) (data *model.TableDataInfo, err error) { - data = new(model.TableDataInfo) - taos, err := s.GetConn(ctx, dbName) + connection, err := sql.Open(connectionMap.tdEngineType, connectionMap.tdDsn+dbName) if err != nil { - err = gerror.New("获取链接失败") - return + return nil, gerror.Wrap(err, "TDengine连接失败") } - defer taos.Close() + // 配置连接池设置 + connection.SetMaxIdleConns(g.Cfg().MustGet(ctx, "tdengine.maxIdleConns").Int()) + connection.SetMaxOpenConns(g.Cfg().MustGet(ctx, "tdengine.maxOpenConns").Int()) - rows, err := taos.Query("SELECT * FROM " + tableName) - if err != nil { - err = gerror.New(err.Error()) - return - } - defer rows.Close() + connectionMap.ConnectionMap[dbName] = connection + return connection, nil +} - //获取查询结果字段 - columns, _ := rows.Columns() - //字段数组 - var filed []string - //封装scanArg - scanArgs := make([]any, len(columns)) - for i := range columns { - filed = append(filed, columns[i]) - scanArgs[i] = &columns[i] - } - data.Filed = append(data.Filed, filed...) - for rows.Next() { - err = rows.Scan(scanArgs...) - if err != nil { - err = gerror.New("获取失败") +// Time REST连接时区处理 +func (s *sTdEngine) Time(v *g.Var) (rs *g.Var) { + driver := g.Cfg().MustGet(context.TODO(), "tdengine.type") + if driver.String() == "taosRestful" { + if t, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", v.String()); err == nil { + rs = gvar.New(t.Local().Format("2006-01-02 15:04:05")) return } - //封装返回结果 - var resultMap = make(map[string]interface{}) - for i := range columns { - resultMap[filed[i]] = columns[i] - } - data.Info = append(data.Info, resultMap) } + rs = v return } -// 超级表查询,单条数据 -func (s *sTdEngine) GetOne(ctx context.Context, sql string, args ...any) (rs gdb.Record, err error) { - taos, err := service.TdEngine().GetConn(ctx, dbName) - if err != nil { - err = gerror.New("获取链接失败") +func Close() { + if connectionMap == nil { return } - defer taos.Close() - - rows, err := taos.Query(sql, args...) - if err != nil { - g.Log().Error(ctx, err, sql, args) - return nil, err - } - defer rows.Close() - - columns, _ := rows.Columns() - values := make([]any, len(columns)) - rs = make(gdb.Record, len(columns)) - for i := range values { - values[i] = new(any) - } - - for rows.Next() { - err = rows.Scan(values...) - if err != nil { - return nil, err - } - - for i, c := range columns { - rs[c] = s.Time(gvar.New(values[i])) + connectionMap.Lock() + for _, node := range connectionMap.ConnectionMap { + if err := node.Close(); err != nil { + return } - - rows.Close() } - - return + connectionMap.Unlock() } -// 超级表查询,多条数据 -func (s *sTdEngine) GetAll(ctx context.Context, sql string, args ...any) (rs gdb.Result, err error) { - taos, err := service.TdEngine().GetConn(ctx, dbName) +// ClearLogByDays 删除指定天数的设备日志数据 +func (s *sTdEngine) ClearLogByDays(ctx context.Context, days int) (err error) { + db, err := s.GetConn(ctx, dbName) if err != nil { - err = gerror.New("获取链接失败") return } - defer taos.Close() - rows, err := taos.Query(sql, args...) + // + brforeTime := gtime.Now().AddDate(0, 0, -days).Format("Y-m-d H:i:s.u") + sqlStr := "delete from device_log where ts < ?" + rows, err := db.Query(sqlStr, brforeTime) if err != nil { - g.Log().Error(ctx, err, sql) - return nil, err + g.Log().Error(ctx, err, sqlStr) + return } defer rows.Close() - - columns, _ := rows.Columns() - - for rows.Next() { - values := make([]any, len(columns)) - for i := range values { - values[i] = new(any) - } - - err = rows.Scan(values...) - if err != nil { - return nil, err - } - - m := make(gdb.Record, len(columns)) - for i, c := range columns { - m[c] = s.Time(gvar.New(values[i])) - } - rs = append(rs, m) - } - - return -} - -// REST连接时区处理 -func (s *sTdEngine) Time(v *g.Var) (rs *g.Var) { - driver, _ := g.Cfg().Get(context.TODO(), "tdengine.type") - - if driver.String() == "taosRestful" { - if t, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", v.String()); err == nil { - rs = gvar.New(t.Local().Format("2006-01-02 15:04:05")) - return - } - } - - rs = v return } diff --git a/internal/logic/tdengine/td_engine_test.go b/internal/logic/tdengine/td_engine_test.go new file mode 100644 index 0000000..69eca20 --- /dev/null +++ b/internal/logic/tdengine/td_engine_test.go @@ -0,0 +1,52 @@ +package tdengine + +import ( + "context" + "reflect" + "sagooiot/pkg/tsd" + "testing" + + "github.com/gogf/gf/v2/database/gdb" + + _ "github.com/taosdata/driver-go/v3/taosRestful" + _ "github.com/taosdata/driver-go/v3/taosWS" +) + +func Test_sTdEngine_GetOne(t *testing.T) { + type args struct { + ctx context.Context + sql string + args []any + } + tests := []struct { + name string + args args + wantRs gdb.Record + wantErr bool + }{ + { + name: "测试统计设备日志总数", + args: args{ + ctx: context.Background(), + sql: "select count(*) as num from ?", + args: []any{"device_log"}, + }, + }, + } + + tsdDb := tsd.DB() + defer tsdDb.Close() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotRs, err := tsdDb.GetTableDataOne(tt.args.ctx, tt.args.sql, tt.args.args...) + if (err != nil) != tt.wantErr { + t.Errorf("GetOne() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotRs, tt.wantRs) { + t.Errorf("GetOne() gotRs = %v, want %v", gotRs, tt.wantRs) + } + }) + } +} diff --git a/internal/logic/tdengine/td_log_table.go b/internal/logic/tdengine/td_log_table.go index 5b35471..c7110f9 100644 --- a/internal/logic/tdengine/td_log_table.go +++ b/internal/logic/tdengine/td_log_table.go @@ -2,8 +2,9 @@ package tdengine import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/tsd/comm" "time" "github.com/gogf/gf/v2/container/gvar" @@ -25,12 +26,24 @@ func tdLogTableNew() *sTdLogTable { // 添加超级表 func (s *sTdLogTable) CreateStable(ctx context.Context) (err error) { + // 资源锁 + lockKey := "tdLock:initLogTable" + lockVal, err := g.Redis().Do(ctx, "SET", lockKey, gtime.Now().Unix(), "NX", "EX", "3600") + if err != nil { + return + } + if lockVal.String() != "OK" { + return + } + defer func() { + _, err = g.Redis().Do(ctx, "DEL", lockKey) + }() + taos, err := service.TdEngine().GetConn(ctx, dbName) if err != nil { err = gerror.New("获取链接失败") return } - defer taos.Close() var name string err = taos.QueryRow("SELECT stable_name FROM information_schema.ins_stables WHERE stable_name = 'device_log' LIMIT 1").Scan(&name) @@ -38,7 +51,7 @@ func (s *sTdLogTable) CreateStable(ctx context.Context) (err error) { return } - sql := "CREATE STABLE device_log (ts TIMESTAMP, type VARCHAR(20), content VARCHAR(1000)) TAGS (device VARCHAR(255))" + sql := "CREATE STABLE device_log (ts TIMESTAMP, type VARCHAR(20), content VARCHAR(5000)) TAGS (device VARCHAR(255))" _, err = taos.Exec(sql) return @@ -51,10 +64,11 @@ func (s *sTdLogTable) Insert(ctx context.Context, log *model.TdLogAddInput) (err err = gerror.New("获取链接失败") return } - defer taos.Close() + + table := comm.DeviceLogTable(log.Device) sql := "INSERT INTO ? USING device_log TAGS ('?') VALUES ('?', '?', '?')" - _, err = taos.Exec(sql, "log_"+log.Device, log.Device, log.Ts.String(), log.Type, log.Content) + _, err = taos.Exec(sql, table, log.Device, log.Ts.String(), log.Type, log.Content) return } @@ -66,7 +80,6 @@ func (s *sTdLogTable) Clear(ctx context.Context) (err error) { err = gerror.New("获取链接失败") return } - defer taos.Close() ts := gtime.Now().Add(-7 * 24 * time.Hour).Format("Y-m-d") @@ -83,7 +96,6 @@ func (s *sTdLogTable) GetAll(ctx context.Context, sql string, args ...any) (list err = gerror.New("获取链接失败") return } - defer taos.Close() rows, err := taos.Query(sql, args...) if err != nil { diff --git a/internal/logic/tdengine/td_log_table_test.go b/internal/logic/tdengine/td_log_table_test.go index 8535912..6efb61c 100644 --- a/internal/logic/tdengine/td_log_table_test.go +++ b/internal/logic/tdengine/td_log_table_test.go @@ -2,9 +2,9 @@ package tdengine import ( "context" - _ "github.com/sagoo-cloud/sagooiot/internal/logic/product" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + _ "sagooiot/internal/logic/product" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" diff --git a/internal/logic/tdengine/tsl_table.go b/internal/logic/tdengine/tsl_table.go index b8e2216..2f1bbff 100644 --- a/internal/logic/tdengine/tsl_table.go +++ b/internal/logic/tdengine/tsl_table.go @@ -3,20 +3,20 @@ package tdengine import ( "context" "fmt" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/tsd/comm" "strconv" "strings" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/gogf/gf/v2/container/gvar" "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" ) // 物模型 TDengine 表结构维护 -type sTSLTable struct { -} +type sTSLTable struct{} func init() { service.RegisterTSLTable(tslTableNew()) @@ -26,16 +26,16 @@ func tslTableNew() *sTSLTable { return &sTSLTable{} } -// 数据入库 -func (s *sTSLTable) Insert(ctx context.Context, deviceKey string, data map[string]any) (err error) { +// Insert 数据入库 +func (s *sTSLTable) Insert(ctx context.Context, deviceKey string, data model.ReportPropertyData, subKey ...string) (err error) { taos, err := service.TdEngine().GetConn(ctx, dbName) if err != nil { err = gerror.New("获取链接失败") return } - defer taos.Close() if len(data) == 0 { + err = gerror.New("上报数据为空") return } @@ -46,13 +46,25 @@ func (s *sTSLTable) Insert(ctx context.Context, deviceKey string, data map[strin value = []string{"'" + ts + "'"} ) for k, v := range data { + k = comm.TsdColumnName(k) + field = append(field, k) - value = append(value, "'"+gvar.New(v).String()+"'") + value = append(value, "'"+gvar.New(v.Value).String()+"'") + // 属性上报时间 + field = append(field, k+"_time") + value = append(value, "'"+gtime.New(v.CreateTime).Format("Y-m-d H:i:s")+"'") } - sql := "INSERT INTO ? (?) VALUES (?)" - _, err = taos.Exec(sql, deviceKey, strings.Join(field, ","), strings.Join(value, ",")) - + if len(subKey) == 0 { + deviceKey = comm.DeviceTableName(deviceKey) + sql := "INSERT INTO ? (?) VALUES (?)" + _, err = taos.Exec(sql, deviceKey, strings.Join(field, ","), strings.Join(value, ",")) + } else { + // 子设备 + skey := comm.DeviceTableName(subKey[0]) + sql := "INSERT INTO ? (?) VALUES (?)" + _, err = taos.Exec(sql, skey, strings.Join(field, ","), strings.Join(value, ",")) + } return } @@ -63,7 +75,6 @@ func (s *sTSLTable) CreateStable(ctx context.Context, tsl *model.TSL) (err error err = gerror.New("获取链接失败") return } - defer taos.Close() // 属性字段 columns := []string{"ts TIMESTAMP"} @@ -73,6 +84,8 @@ func (s *sTSLTable) CreateStable(ctx context.Context, tsl *model.TSL) (err error maxLength = *v.ValueType.TSLParamBase.MaxLength } columns = append(columns, s.column(v.ValueType.Type, v.Key, v.Name, maxLength)) + // 属性上报时间 + columns = append(columns, s.column("date", v.Key+"_time", "", 0)) } // 标签字段 @@ -83,14 +96,15 @@ func (s *sTSLTable) CreateStable(ctx context.Context, tsl *model.TSL) (err error if v.ValueType.TSLParamBase.MaxLength != nil { maxLength = *v.ValueType.TSLParamBase.MaxLength } - tags[i+1] = s.column(v.ValueType.Type, v.Key, v.Name, maxLength) + tags[i+1] = s.column(v.ValueType.Type, v.Key, v.Name, maxLength, 1) } tConent := "" if len(tags) > 0 { tConent = fmt.Sprintf("TAGS (%s)", strings.Join(tags, ",")) } - sql := fmt.Sprintf("CREATE STABLE %s.%s (%s) %s", dbName, tsl.Key, strings.Join(columns, ","), tConent) + table := comm.ProductTableName(tsl.Key) + sql := fmt.Sprintf("CREATE STABLE IF NOT EXISTS %s.%s (%s) %s", dbName, table, strings.Join(columns, ","), tConent) _, err = taos.Exec(sql) @@ -104,17 +118,19 @@ func (s *sTSLTable) CreateTable(ctx context.Context, stable, table string) (err err = gerror.New("获取链接失败") return } - defer taos.Close() - sql := fmt.Sprintf("CREATE TABLE %s USING %s (device) TAGS ('%s')", table, stable, table) - println(sql) + tag := table + stable = comm.ProductTableName(stable) + table = comm.DeviceTableName(table) + + sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s USING %s (device) TAGS ('%s')", table, stable, tag) _, err = taos.Exec(sql) return } -func (s *sTSLTable) column(dataType, key, name string, maxLength int) string { +func (s *sTSLTable) column(dataType, key, name string, maxLength int, isTag ...int) string { column := "" tdType := "" switch dataType { @@ -128,7 +144,7 @@ func (s *sTSLTable) column(dataType, key, name string, maxLength int) string { tdType = "DOUBLE" case "string": if maxLength == 0 { - maxLength = 255 + maxLength = 150 } tdType = "NCHAR(" + strconv.Itoa(maxLength) + ")" case "boolean": @@ -137,24 +153,33 @@ func (s *sTSLTable) column(dataType, key, name string, maxLength int) string { tdType = "TIMESTAMP" default: if maxLength == 0 { - maxLength = 255 + maxLength = 150 } tdType = "NCHAR(" + strconv.Itoa(maxLength) + ")" } + + // 属性、tag加前缀 + if len(isTag) > 0 && isTag[0] == 1 { + key = comm.TsdTagName(key) + } else { + key = comm.TsdColumnName(key) + } + column = fmt.Sprintf("%s %s", key, tdType) return column } // 删除超级表 -func (s *sTSLTable) DropStable(ctx context.Context, table string) (err error) { +func (s *sTSLTable) DropStable(ctx context.Context, stable string) (err error) { taos, err := service.TdEngine().GetConn(ctx, "") if err != nil { err = gerror.New("获取链接失败") return } - defer taos.Close() - sql := fmt.Sprintf("DROP STABLE IF EXISTS %s.%s", dbName, table) + stable = comm.ProductTableName(stable) + + sql := fmt.Sprintf("DROP STABLE IF EXISTS %s.%s", dbName, stable) _, err = taos.Exec(sql) return @@ -167,7 +192,8 @@ func (s *sTSLTable) DropTable(ctx context.Context, table string) (err error) { err = gerror.New("获取链接失败") return } - defer taos.Close() + + table = comm.DeviceTableName(table) sql := fmt.Sprintf("DROP TABLE IF EXISTS %s.%s", dbName, table) _, err = taos.Exec(sql) @@ -177,24 +203,79 @@ func (s *sTSLTable) DropTable(ctx context.Context, table string) (err error) { // 创建数据库 func (s *sTSLTable) CreateDatabase(ctx context.Context) (err error) { + // 资源锁 + lockKey := "tdLock:initDb" + lockVal, err := g.Redis().Do(ctx, "SET", lockKey, gtime.Now().Unix(), "NX", "EX", "3600") + if err != nil { + return + } + if lockVal.String() != "OK" { + return + } + defer func() { + _, err = g.Redis().Do(ctx, "DEL", lockKey) + }() + taos, err := service.TdEngine().GetConn(ctx, "") if err != nil { err = gerror.New("获取链接失败") return } - defer taos.Close() - var name string - taos.QueryRow("SELECT name FROM information_schema.ins_databases WHERE name = '?' LIMIT 1", dbName).Scan(&name) - if name != "" { + /*var name string + if err = taos.QueryRow("SELECT name FROM information_schema.ins_databases WHERE name = '?' LIMIT 1", dbName).Scan(&name); err != nil { return } + if name != "" { + return + }*/ + _, err = taos.Exec("CREATE DATABASE IF NOT EXISTS " + dbName) return } +// CheckStable 查询超级表是否存在, true=存在 +func (s *sTSLTable) CheckStable(ctx context.Context, stable string) (b bool, err error) { + taos, err := service.TdEngine().GetConn(ctx, "") + if err != nil { + err = gerror.New("获取链接失败") + return + } + + var name string + if err = taos.QueryRow("SELECT stable_name FROM information_schema.ins_stables WHERE stable_name = '?' LIMIT 1", stable).Scan(&name); err != nil { + return + } + if name == "" { + return + } + + b = true + return +} + +// CheckTable 查询子表是否存在, true=存在 +func (s *sTSLTable) CheckTable(ctx context.Context, table string) (b bool, err error) { + taos, err := service.TdEngine().GetConn(ctx, "") + if err != nil { + err = gerror.New("获取链接失败") + return + } + + var name string + if err = taos.QueryRowContext(ctx, "SELECT table_name FROM information_schema.ins_tables WHERE table_name = '?' LIMIT 1", table).Scan(&name); err != nil { + return + } + if name == "" { + return + } + + b = true + return +} + // AddDatabaseField 添加数据库字段 func (s *sTSLTable) AddDatabaseField(ctx context.Context, tableName, fieldName string, dataType string, len int) (err error) { taos, err := service.TdEngine().GetConn(ctx, "") @@ -202,9 +283,15 @@ func (s *sTSLTable) AddDatabaseField(ctx context.Context, tableName, fieldName s err = gerror.New("获取链接失败") return } - defer taos.Close() + + tableName = comm.ProductTableName(tableName) sql := fmt.Sprintf("ALTER STABLE %s.%s ADD COLUMN %s", dbName, tableName, s.column(dataType, fieldName, "", len)) + if _, err = taos.Exec(sql); err != nil { + return + } + // 属性上报时间 + sql = fmt.Sprintf("ALTER STABLE %s.%s ADD COLUMN %s", dbName, tableName, s.column("date", fieldName+"_time", "", 0)) _, err = taos.Exec(sql) return @@ -217,9 +304,16 @@ func (s *sTSLTable) DelDatabaseField(ctx context.Context, tableName, fieldName s err = gerror.New("获取链接失败") return } - defer taos.Close() + + tableName = comm.ProductTableName(tableName) + fieldName = comm.TsdColumnName(fieldName) sql := fmt.Sprintf("ALTER STABLE %s.%s DROP COLUMN %s", dbName, tableName, fieldName) + if _, err = taos.Exec(sql); err != nil { + return + } + // 属性上报时间 + sql = fmt.Sprintf("ALTER STABLE %s.%s DROP COLUMN %s", dbName, tableName, fieldName+"_time") _, err = taos.Exec(sql) return @@ -232,7 +326,8 @@ func (s *sTSLTable) ModifyDatabaseField(ctx context.Context, tableName, fieldNam err = gerror.New("获取链接失败") return } - defer taos.Close() + + tableName = comm.ProductTableName(tableName) sql := fmt.Sprintf("ALTER STABLE %s.%s MODIFY COLUMN %s", dbName, tableName, s.column(dataType, fieldName, "", len)) _, err = taos.Exec(sql) @@ -250,9 +345,10 @@ func (s *sTSLTable) AddTag(ctx context.Context, tableName, tagName string, dataT err = gerror.New("获取链接失败") return } - defer taos.Close() - sql := fmt.Sprintf("ALTER STABLE %s.%s ADD TAG %s", dbName, tableName, s.column(dataType, tagName, "", len)) + tableName = comm.ProductTableName(tableName) + + sql := fmt.Sprintf("ALTER STABLE %s.%s ADD TAG %s", dbName, tableName, s.column(dataType, tagName, "", len, 1)) _, err = taos.Exec(sql) return @@ -265,7 +361,9 @@ func (s *sTSLTable) DelTag(ctx context.Context, tableName, tagName string) (err err = gerror.New("获取链接失败") return } - defer taos.Close() + + tableName = comm.ProductTableName(tableName) + tagName = comm.TsdTagName(tagName) sql := fmt.Sprintf("ALTER STABLE %s.%s DROP TAG %s", dbName, tableName, tagName) _, err = taos.Exec(sql) @@ -280,9 +378,10 @@ func (s *sTSLTable) ModifyTag(ctx context.Context, tableName, tagName string, da err = gerror.New("获取链接失败") return } - defer taos.Close() - sql := fmt.Sprintf("ALTER STABLE %s.%s MODIFY TAG %s", dbName, tableName, s.column(dataType, tagName, "", len)) + tableName = comm.ProductTableName(tableName) + + sql := fmt.Sprintf("ALTER STABLE %s.%s MODIFY TAG %s", dbName, tableName, s.column(dataType, tagName, "", len, 1)) _, err = taos.Exec(sql) if err != nil { err = gerror.New("设置标签长度失败,长度只能增大不能缩小") diff --git a/internal/logic/tdengine/tsl_table_test.go b/internal/logic/tdengine/tsl_table_test.go index bf95125..d3d396b 100644 --- a/internal/logic/tdengine/tsl_table_test.go +++ b/internal/logic/tdengine/tsl_table_test.go @@ -3,8 +3,8 @@ package tdengine import ( "context" "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" + "sagooiot/internal/model" + "sagooiot/internal/service" "testing" _ "github.com/gogf/gf/contrib/drivers/mysql/v2" @@ -13,13 +13,9 @@ import ( func TestInsertTSL(t *testing.T) { deviceKey := "k213213" - data := map[string]any{ - "ts": gtime.Now(), - "property_99": 2, - "property_98": 2, - "property_97": 2, - "property_96": 2, - "property_95": 2, + data := model.ReportPropertyData{ + "property_99": {2, gtime.Now().Unix()}, + "property_98": {2, gtime.Now().Unix()}, } err := service.TSLTable().Insert(context.TODO(), deviceKey, data) if err != nil { @@ -28,7 +24,7 @@ func TestInsertTSL(t *testing.T) { } func TestCreateStable(t *testing.T) { - metadata := `{"key":"product_cc","name":"产品_1","properties":[{"key":"property_1","name":"属性_1","accessMode":1,"valueType":{"type":"string","maxLength":0},"desc":"描述edit"}],"functions":[{"key":"function_3","name":"功能_3","inputs":[{"key":"input_1","name":"参数_1","valueType":{"type":"string","maxLength":22},"desc":"参数描述"}],"output":{"type":"string","maxLength":22},"desc":"描述编辑"}],"events":[{"key":"function_1","name":"事件_1","level":0,"valueType":{"type":"string","maxLength":22},"desc":"描述"}],"tags":[]}` + metadata := `{"key":"product_tsl_adjust","name":"物模型调整","properties":[{"key":"property_1","name":"属性_1","accessMode":1,"valueType":{"type":"string","maxLength":0},"desc":"描述edit"}],"functions":[],"events":[],"tags":[]}` var tsl *model.TSL err := json.Unmarshal([]byte(metadata), &tsl) @@ -50,7 +46,14 @@ func TestDropStable(t *testing.T) { } func TestAddDatabaseField(t *testing.T) { - err := service.TSLTable().AddDatabaseField(context.TODO(), "product_cc", "test_add", "int", 0) + err := service.TSLTable().AddDatabaseField(context.TODO(), "product_tsl_adjust", "test_add", "int", 0) + if err != nil { + t.Fatal(err) + } +} + +func TestDelDatabaseField(t *testing.T) { + err := service.TSLTable().DelDatabaseField(context.TODO(), "product_tsl_adjust", "test_add") if err != nil { t.Fatal(err) } diff --git a/internal/model/alarm_level.go b/internal/model/alarm_level.go index fc680f8..898ceac 100644 --- a/internal/model/alarm_level.go +++ b/internal/model/alarm_level.go @@ -1,6 +1,6 @@ package model -import "github.com/sagoo-cloud/sagooiot/internal/model/entity" +import "sagooiot/internal/model/entity" type AlarmLevelOutput struct { *entity.AlarmLevel diff --git a/internal/model/alarm_log.go b/internal/model/alarm_log.go index 34b4623..efd64f9 100644 --- a/internal/model/alarm_log.go +++ b/internal/model/alarm_log.go @@ -1,7 +1,7 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "sagooiot/internal/model/entity" ) const ( @@ -26,6 +26,7 @@ type AlarmLogAddInput struct { RuleName string `json:"ruleName" dc:"规则名称"` Level uint `json:"level" dc:"告警级别"` Data string `json:"data" dc:"触发告警的数据"` + Expression string `json:"expression" dc:"触发告警的表达式"` ProductKey string `json:"productKey" dc:"产品标识"` DeviceKey string `json:"deviceKey" dc:"设备标识"` } @@ -47,8 +48,12 @@ type AlarmLogLevelTotal struct { // 日志列表 type AlarmLogListInput struct { + AlarmInput PaginationInput } +type AlarmInput struct { + Status string `p:"status"` //告警状态 +} type AlarmLogListOutput struct { List []AlarmLogOutput `json:"list" dc:"告警日志"` PaginationOutput diff --git a/internal/model/alarm_rule.go b/internal/model/alarm_rule.go index af92ff2..71de92d 100644 --- a/internal/model/alarm_rule.go +++ b/internal/model/alarm_rule.go @@ -1,53 +1,41 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" -) - -const ( - AlarmTriggerTypeOnline = iota + 1 // 触发类型:设备上线 - AlarmTriggerTypeOffline // 触发类型:设备离线 - AlarmTriggerTypeProperty // 触发类型:属性上报 - AlarmTriggerTypeEvent // 触发类型:事件上报 + "sagooiot/internal/consts" + "sagooiot/internal/model/entity" ) var AlarmTriggerType = map[int]string{ - AlarmTriggerTypeOnline: "设备上线", - AlarmTriggerTypeOffline: "设备离线", - AlarmTriggerTypeProperty: "属性上报", - AlarmTriggerTypeEvent: "事件上报", + consts.AlarmTriggerTypeOnline: "设备上线", + consts.AlarmTriggerTypeOffline: "设备离线", + consts.AlarmTriggerTypeProperty: "属性上报", + consts.AlarmTriggerTypeEvent: "事件上报", } -const ( - OperatorEq = "eq" // 操作符:等于 - OperatorNe = "ne" // 操作符:不等于 - OperatorGt = "gt" // 操作符:大于 - OperatorGte = "gte" // 操作符:大于等于 - OperatorLt = "lt" // 操作符:小于 - OperatorLte = "lte" // 操作符:小于等于 - OperatorBet = "bet" // 操作符:在...之间 - OperatorNbet = "nbet" // 操作符:不在...之间 +// 设备触发条件 +type ( + AlarmFilters struct { + Key string `json:"key" dc:"条件key"` + Operator string `json:"operator" dc:"操作符:eq,ne,gt,lt,gte,lte,bet,nbet"` + Value []string `json:"value" dc:"条件值"` + AndOr int `json:"andOr" dc:"多个条件参数的关系:0=无,1=并且,2=或"` + } + AlarmCondition struct { + Filters []AlarmFilters `json:"filters" dc:"条件参数"` + AndOr int `json:"andOr" dc:"多个条件组的关系:0=无,1=并且,2=或"` + } + AlarmTriggerCondition struct { + TriggerCondition []AlarmCondition `json:"triggerCondition" dc:"触发条件" v:"required-unless:triggerType,1,triggerType,2#请添加触发条件"` + } ) -const ( - AlarmRuleStatusOff int = iota // 告警规则状态:未启用 - AlarmRuleStatusOn // 告警规则状态:已启用 +// 定时触发条件 +type ( + AlarmCronCondition struct { + CronCondition []string `json:"cronCondition" dc:"定时表达式"` + } ) -type AlarmFilters struct { - Key string `json:"key" dc:"条件key"` - Operator string `json:"operator" dc:"操作符:eq,ne,gt,lt,gte,lte,bet,nbet"` - Value []string `json:"value" dc:"条件值"` - AndOr int `json:"andOr" dc:"多个条件参数的关系:0=无,1=并且,2=或"` -} -type AlarmCondition struct { - Filters []AlarmFilters `json:"filters" dc:"条件参数"` - AndOr int `json:"andOr" dc:"多个条件组的关系:0=无,1=并且,2=或"` -} -type AlarmTriggerCondition struct { - TriggerCondition []AlarmCondition `json:"triggerCondition" dc:"触发条件" v:"required#请添加触发条件"` -} - type AlarmAction struct { SendGateway string `json:"sendGateway" dc:"通知发送通道:sms、work_weixin、dingding"` NoticeConfig string `json:"noticeConfig" dc:"通知配置"` @@ -63,7 +51,8 @@ type AlarmRuleAddInput struct { Level uint `json:"level" dc:"告警级别" v:"required#请选择告警级别"` ProductKey string `json:"productKey" dc:"产品标识" v:"required#请选择产品"` DeviceKey string `json:"deviceKey" dc:"设备标识"` - TriggerType int `json:"triggerType" dc:"触发类型:1=上线,2=离线,3=属性上报" v:"required#请选择触发类型"` + TriggerType int `json:"triggerType" dc:"触发类型:1=上线,2=离线,3=属性上报,4=事件上报" v:"required#请选择触发类型"` + EventKey string `json:"eventKey" dc:"事件标识" v:"required-if:triggerType,4#请选择事件"` AlarmTriggerCondition AlarmPerformAction } @@ -78,7 +67,8 @@ type AlarmRuleOutput struct { TriggerTypeName string `json:"triggerTypeName" dc:"触发类型"` - Condition AlarmTriggerCondition `json:"condition" dc:"触发条件"` + Condition AlarmTriggerCondition `json:"condition" dc:"设备触发条件"` + CronCondition AlarmCronCondition `json:"cronCondition" dc:"定时触发条件"` PerformAction AlarmPerformAction `json:"performAction" dc:"执行动作"` @@ -110,3 +100,17 @@ type AlarmRuleListOutput struct { List []AlarmRuleOutput `json:"list" dc:"告警规则列表"` PaginationOutput } + +type AlarmCronRuleAddInput struct { + Name string `json:"name" dc:"告警规则名称" v:"required#请输入告警规则名称"` + Level uint `json:"level" dc:"告警级别" v:"required#请选择告警级别"` + ProductKey string `json:"productKey" dc:"产品标识" v:"required#请选择产品"` + DeviceKey string `json:"deviceKey" dc:"设备标识"` + AlarmCronCondition + AlarmPerformAction +} + +type AlarmCronRuleEditInput struct { + Id uint64 `json:"id" dc:"告警规则ID" v:"required#告警规则ID不能为空"` + AlarmCronRuleAddInput +} diff --git a/internal/model/analysis.go b/internal/model/analysis.go new file mode 100644 index 0000000..7061fe4 --- /dev/null +++ b/internal/model/analysis.go @@ -0,0 +1,37 @@ +package model + +import "sagooiot/pkg/iotModel" + +// DeviceOnlineOfflineCount 设备在线离线状态统计 +type DeviceOnlineOfflineCount struct { + Total int `json:"total"` // 设备总数 + Online int `json:"online"` // 在线设备数 + Offline int `json:"offline"` // 离线设备数 + Disable int `json:"disable"` // 禁用设备数 +} + +// CountData 统计数据 +type CountData struct { + Title string + Value int64 +} + +// ProductCountRes 产品数量统计 +type ProductCountRes struct { + Total int `json:"total"` // 产品总数 + Disable int `json:"disable"` // 禁用产品数 + Enable int `json:"enable"` // 启用产品数 + Added int `json:"added"` // 新增产品数 +} + +// DeviceDataReq 设备数据请求 +type DeviceDataReq struct { + DeviceKey string `json:"deviceKey" v:"required#设备key不能为空" dc:"设备key"` + PaginationInput +} + +// DeviceDataRes 设备数据响应 +type DeviceDataRes struct { + DeviceKey string `json:"deviceKey"` + DeviceData iotModel.ReportPropertyData `json:"deviceData"` +} diff --git a/internal/model/base_db_link.go b/internal/model/base_db_link.go deleted file mode 100644 index d874d30..0000000 --- a/internal/model/base_db_link.go +++ /dev/null @@ -1,82 +0,0 @@ -package model - -import "github.com/gogf/gf/v2/os/gtime" - -type BaseDbLinkDoInput struct { - Name string `p:"name" description:"数据源名称"` - Types string `p:"types" description:"驱动类型 mysql或oracle"` - Host string `p:"host" description:"主机地址"` - Port string `p:"port" description:"端口"` - UserName string `p:"user_name" description:"用户名称"` - Status int `p:"status" description:"状态:-1为全部,0为正常,1为停用"` - *PaginationInput -} - -type BaseDbLinkOut struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle"` - Host string `json:"host" description:"主机地址"` - Port int `json:"port" description:"端口号"` - UserName string `json:"userName" description:"用户名称"` - Password string `json:"password" description:"密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreatedBy uint `json:"createdBy" description:"创建人"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` -} - -// BaseDbLinkRes 数据源列表返回字段 -type BaseDbLinkRes struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle"` - Host string `json:"host" description:"主机地址"` - Port int `json:"port" description:"端口号"` - UserName string `json:"userName" description:"用户名称"` - Password string `json:"password" description:"密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreatedBy uint `json:"createdBy" description:"创建人"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` -} - -type DetailBaseDbLinkRes struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle"` - Host string `json:"host" description:"主机地址"` - Port int `json:"port" description:"端口号"` - UserName string `json:"userName" description:"用户名称"` - Password string `json:"password" description:"密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreatedBy uint `json:"createdBy" description:"创建人"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` -} - -type AddBaseDbLinkInput struct { - Name string `json:"name" description:"名称" v:"required#请输入数据源名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle" v:"required#请输入数据源驱动类型"` - Host string `json:"host" description:"主机地址" v:"required#请输入数据源主机地址"` - Port int `json:"port" description:"端口号" v:"required#请输入数据源端口号"` - UserName string `json:"userName" description:"用户名称" v:"required#请输入数据源用户名称"` - Password string `json:"password" description:"密码" v:"required#请输入数据源密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} - -type EditBaseDbLinkInput struct { - Id int `json:"id" description:"" v:"required#请输入数据源ID"` - Name string `json:"name" description:"名称" v:"required#请输入数据源名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle" v:"required#请输入数据源驱动类型"` - Host string `json:"host" description:"主机地址" v:"required#请输入数据源主机地址"` - Port int `json:"port" description:"端口号" v:"required#请输入数据源端口号"` - UserName string `json:"userName" description:"用户名称" v:"required#请输入数据源用户名称"` - Password string `json:"password" description:"密码" v:"required#请输入数据源密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` -} diff --git a/internal/model/base_model.go b/internal/model/base_model.go index be2a4bc..22a26e9 100644 --- a/internal/model/base_model.go +++ b/internal/model/base_model.go @@ -5,7 +5,7 @@ type PaginationInput struct { DateRange []string `p:"dateRange"` //日期范围 OrderBy string //排序方式 PageNum int `json:"pageNum" in:"query" d:"1" v:"min:0#分页号码错误" dc:"分页号码,默认1"` - PageSize int `json:"PageSize" in:"query" d:"10" v:"max:50#分页数量最大50条" dc:"分页数量,最大50"` + PageSize int `json:"PageSize" in:"query" d:"10" v:"max:500#分页数量最大500条" dc:"分页数量,最大500"` } type PaginationOutput struct { diff --git a/internal/model/city_data.go b/internal/model/city_data.go deleted file mode 100644 index d24aa6d..0000000 --- a/internal/model/city_data.go +++ /dev/null @@ -1,53 +0,0 @@ -package model - -import "github.com/gogf/gf/v2/os/gtime" - -type CityTreeRes struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - ParentId int `json:"parentId" description:"父ID"` - Sort int `json:"sort" description:"排序"` - Status uint `json:"status" description:"状态;0:禁用;1:正常"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` - DeletedBy int `json:"deletedBy" description:"删除人"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` - Children []*CityTreeRes `json:"children" description:"子集"` -} - -type AddCityReq struct { - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - ParentId int `json:"parentId" description:"父ID"` - Sort int `json:"sort" description:"排序"` - Status uint `json:"status" description:"状态;0:禁用;1:正常"` -} - -type EditCityReq struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - ParentId int `json:"parentId" description:"父ID"` - Sort int `json:"sort" description:"排序"` - Status uint `json:"status" description:"状态;0:禁用;1:正常"` -} - -type CityRes struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - ParentId int `json:"parentId" description:"父ID"` - Sort int `json:"sort" description:"排序"` - Status uint `json:"status" description:"状态;0:禁用;1:正常"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` - DeletedBy int `json:"deletedBy" description:"删除人"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/config_data.go b/internal/model/config_data.go index 3b4ab3b..3dca98c 100644 --- a/internal/model/config_data.go +++ b/internal/model/config_data.go @@ -7,7 +7,7 @@ type ConfigDoInput struct { ConfigKey string `p:"configKey"` //参数键名 ConfigType string `p:"configType"` //状态 ModuleClassify string `p:"moduleClassify"` //字典分类编码 - *PaginationInput + PaginationInput } type SysConfigRes struct { @@ -17,8 +17,8 @@ type SysConfigRes struct { ConfigValue string `json:"configValue" description:"参数键值"` ConfigType int `json:"configType" description:"系统内置(Y是 N否)"` ModuleClassify string `json:"moduleClassify" description:"字典分类编码"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` @@ -31,8 +31,8 @@ type SysConfigOut struct { ConfigValue string `json:"configValue" description:"参数键值"` ConfigType int `json:"configType" description:"系统内置(Y是 N否)"` ModuleClassify string `json:"moduleClassify" description:"字典分类编码"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` @@ -56,3 +56,15 @@ type EditConfigInput struct { Remark string `p:"remark"` ModuleClassify string `p:"moduleClassify"` } + +type EditConfigReq struct { + ConfigId int `p:"configId" v:"required#ID不能为空"` + ConfigKey string `p:"configKey" v:"required#KEY不能为空"` + ConfigValue string `p:"configValue" v:"required#值不能为空"` +} + +// CacheConfig 缓存配置 +type CacheConfig struct { + Adapter string `json:"adapter"` + FileDir string `json:"fileDir"` +} diff --git a/internal/model/context.go b/internal/model/context.go index 2ebe96c..065e111 100644 --- a/internal/model/context.go +++ b/internal/model/context.go @@ -15,7 +15,7 @@ type Context struct { // ContextUser 请求上下文中的用户信息 type ContextUser struct { Id int // 用户ID - Passport string // 用户账号 + UserName string // 用户账号 Nickname string // 用户名称 Avatar string // 用户 IsAdmin bool // 是否是管理员 diff --git a/internal/model/data_node.go b/internal/model/data_node.go deleted file mode 100644 index 131832f..0000000 --- a/internal/model/data_node.go +++ /dev/null @@ -1,29 +0,0 @@ -package model - -import "github.com/sagoo-cloud/sagooiot/internal/model/entity" - -// 添加节点 -type DataNodeAddInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Key string `json:"key" dc:"数据节点标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入数据节点标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"数据节点名称" v:"required#请输入数据节点名称"` - DataType string `json:"dataType" dc:"数据类型" v:"required#请选择数据类型"` - Value string `json:"value" dc:"取值项" v:"required#请输入取值项"` - IsPk int `json:"isPk" dc:"是否主键"` - - Rule []DataSourceRule `json:"rule" dc:"规则配置"` -} - -// 编辑节点 -type DataNodeEditInput struct { - NodeId uint64 `json:"nodeId" dc:"数据节点ID" v:"required#数据节点ID不能为空"` - Name string `json:"name" dc:"数据节点名称" v:"required#请输入数据节点名称"` - Value string `json:"value" dc:"取值项" v:"required#请输入取值项"` -} - -// 数据节点 -type DataNodeOutput struct { - *entity.DataNode - - NodeRule []*DataSourceRule `json:"nodeRule" dc:"数据节点规则配置"` -} diff --git a/internal/model/data_source.go b/internal/model/data_source.go deleted file mode 100644 index 70bf701..0000000 --- a/internal/model/data_source.go +++ /dev/null @@ -1,188 +0,0 @@ -package model - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" -) - -const ( - DataSourceFromApi = iota + 1 // api数据源 - DataSourceFromDb // 数据库数据源 - DataSourceFromFile // 文件数据源 - DataSourceFromDevice // 设备数据源 -) - -const ( - DataSourceStatusOff int = iota // 数据源未发布 - DataSourceStatusOn // 数据源已发布 -) - -const ( - DataSourceDbQueryType = "tableName" // 数据库源获取数据方式:表 - DataSourceDbQueryTypeSql = "sql" // 数据库源获取数据方式:sql -) - -// 规则配置 -type DataSourceRule struct { - Expression string `json:"expression" dc:"正则表达式"` - Replace string `json:"replace" dc:"替换内容"` -} - -// 数据源 -type DataSource struct { - Key string `json:"key" dc:"数据源标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入数据源标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"数据源名称" v:"required#请输入数据源名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - From int `json:"from" dc:"数据来源:1=api导入,2=数据库,3=文件,4=设备" v:"required|in:1,2,3,4#请选择数据来源|未知数据来源,请正确选择"` - - Rule []DataSourceRule `json:"rule" dc:"规则配置"` -} - -// api 数据源配置 -type DataSourceConfigApi struct { - Method string `json:"method" dc:"请求方法(get、post、put)"` - Url string `json:"url" dc:"请求地址" v:"url"` - RequestParams [][]DataSourceApiRequestParam `json:"requestParams" dc:"请求参数"` - - // 数据更新间隔,cron 格式 - CronExpression string `json:"cronExpression" dc:"任务执行表达式" v:"required#任务表达式不能为空"` -} - -// api 请求参数 -type DataSourceApiRequestParam struct { - Type string `json:"type" dc:"参数类型(header、body、param)" v:"required|in:header,body,param#请选择参数类型|未知参数类型,请正确选择"` - Key string `json:"key" dc:"参数标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入参数标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"参数标题" v:"required#请输入参数标题"` - Value string `json:"value" dc:"参数值" v:"required#请输入参数值"` -} - -// 搜索数据源 -type DataSourceSearchInput struct { - Key string `json:"key" dc:"数据源标识"` - Name string `json:"name" dc:"数据源名称"` - From int `json:"from" dc:"数据来源" d:"1"` - PaginationInput -} -type DataSourceSearchOutput struct { - List []entity.DataSource `json:"list" dc:"数据源列表"` - PaginationOutput -} - -// 添加 api 数据源 -type DataSourceApiAddInput struct { - DataSource - - Config DataSourceConfigApi `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 编辑 api 数据源 -type DataSourceApiEditInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Name string `json:"name" dc:"数据源名称" v:"required#请输入数据源名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Key string `json:"key" dc:"数据源标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - - Rule []DataSourceRule `json:"rule" dc:"规则配置"` - - Config DataSourceConfigApi `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 数据源详情 -type DataSourceOutput struct { - *entity.DataSource - - SourceRule []*DataSourceRule `json:"sourceRule" dc:"数据源规则配置"` - - ApiConfig *DataSourceConfigApi `json:"apiConfig,omitempty" dc:"api配置"` - DeviceConfig *DataSourceConfigDevice `json:"deviceConfig,omitempty" dc:"设备配置"` - DbConfig *DataSourceConfigDb `json:"dbConfig,omitempty" dc:"数据库配置"` -} - -// 设备 数据源配置 -type DataSourceConfigDevice struct { - ProductKey string `json:"productKey" dc:"产品标识"` - DeviceKey string `json:"deviceKey" dc:"设备标识"` -} - -// 添加 设备 数据源 -type DataSourceDeviceAddInput struct { - DataSource - - Config DataSourceConfigDevice `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 编辑 设备 数据源 -type DataSourceDeviceEditInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Name string `json:"name" dc:"数据源名称" v:"required#请输入数据源名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Key string `json:"key" dc:"数据源标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - - Rule []DataSourceRule `json:"rule" dc:"规则配置"` - - Config DataSourceConfigDevice `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 数据库 数据源配置 -type DataSourceConfigDb struct { - Type string `json:"type" dc:"数据库类型(mysql/mssql)" v:"required#请配置数据库类型"` - Host string `json:"host" dc:"主机" v:"required#请配置主机地址"` - Port int `json:"port" dc:"端口" v:"required#请配置端口号"` - User string `json:"user" dc:"用户名" v:"required#请配置用户名"` - Passwd string `json:"passwd" dc:"密码" v:"required#请配置密码"` - DbName string `json:"dbName" dc:"数据库名称" v:"required#请配置数据库名称"` - - QueryType string `json:"queryType" dc:"数据获取方式" v:"required|in:tableName,sql#请选择数据获取方式|请正确选择数据获取方式"` - TableName string `json:"tableName" dc:"表名称" v:"required#请配置表名称或sql语句"` - - Pk string `json:"pk" dc:"主键字段"` - Num int `json:"num" dc:"每次获取数量" d:"100"` - - PkMax uint64 `json:"pkmax" dc:"主键最大值"` - - // 数据更新间隔,cron 格式 - CronExpression string `json:"cronExpression" dc:"任务执行表达式" v:"required#任务表达式不能为空"` -} - -// 添加 数据库 数据源 -type DataSourceDbAddInput struct { - DataSource - - Config DataSourceConfigDb `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 编辑 数据库 数据源 -type DataSourceDbEditInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Name string `json:"name" dc:"数据源名称" v:"required#请输入数据源名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Key string `json:"key" dc:"数据源标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - - Rule []DataSourceRule `json:"rule" dc:"规则配置"` - - Config DataSourceConfigDb `json:"config" dc:"数据源配置" v:"required#请配置数据源"` -} - -// 数据源获取数据的内网方法列表,供大屏使用 -type AllSourceOut struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID"` - Name string `json:"name" dc:"数据源名称"` - Path string `json:"path" dc:"接口地址"` -} - -type SourceDataAllInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Param map[string]interface{} `json:"param" dc:"搜索哪些字段的数据"` -} -type SourceDataAllOutput struct { - List string `json:"data" dc:"源数据记录"` -} - -type DataSourceDataInput struct { - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required#数据源ID不能为空"` - Param map[string]interface{} `json:"param" dc:"搜索哪些字段的数据"` - PaginationInput -} -type DataSourceDataOutput struct { - List string `json:"data" dc:"源数据记录"` - PaginationOutput -} diff --git a/internal/model/data_template.go b/internal/model/data_template.go deleted file mode 100644 index c569f26..0000000 --- a/internal/model/data_template.go +++ /dev/null @@ -1,106 +0,0 @@ -package model - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - - "github.com/gogf/gf/v2/frame/g" -) - -const ( - DataTemplateStatusOff int = iota // 数据模型未发布 - DataTemplateStatusOn // 数据模型已发布 -) - -type DataTemplate struct { - *entity.DataTemplate - - // 绑定的业务 - DataTemplateBusi []DataTemplateBusi `json:"dataTemplateBusi" orm:"with:data_template_id=id" dc:"绑定的业务单元"` - BusiTypes []int `json:"busiTypes" dc:"业务单元"` -} - -// 绑定业务模型 -type DataTemplateBusi struct { - DataTemplateId uint64 `json:"dataTemplateId" dc:"数据模型ID"` - BusiTypes int `json:"busiTypes" dc:"业务单元"` -} - -// 搜索数据模型 -type DataTemplateSearchInput struct { - Key string `json:"key" dc:"数据模型标识"` - Name string `json:"name" dc:"数据模型名称"` - PaginationInput -} -type DataTemplateSearchOutput struct { - List []DataTemplate `json:"list" dc:"数据模型列表"` - PaginationOutput -} - -// 添加数据模型 -type DataTemplateAddInput struct { - Key string `json:"key" dc:"数据模型标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入数据模型标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"数据模型名称" v:"required#请输入数据模型名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - - // 数据更新间隔,cron 格式 - CronExpression string `json:"cronExpression" dc:"任务执行表达式" v:"required#任务表达式不能为空"` -} - -// 编辑数据模型 -type DataTemplateEditInput struct { - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - Name string `json:"name" dc:"数据模型名称" v:"required#请输入数据模型名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Key string `json:"key" dc:"数据模型标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - - // 数据更新间隔,cron 格式 - CronExpression string `json:"cronExpression" dc:"任务执行表达式" v:"required#任务表达式不能为空"` - - // 绑定业务 - BusiTypes []int `json:"busiTypes" dc:"业务单元"` -} - -// 数据模型 -type DataTemplateOutput struct { - *entity.DataTemplate -} - -// 数据模型获取数据的内网方法列表,供大屏使用 -type AllTemplateOut struct { - Id uint64 `json:"id" dc:"数据模型ID"` - Name string `json:"name" dc:"数据模型名称"` - Path string `json:"path" dc:"接口地址"` -} - -type TemplateDataAllInput struct { - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - Param map[string]interface{} `json:"param" dc:"搜索哪些字段的数据"` -} -type TemplateDataAllOutput struct { - List g.List `json:"data" dc:"模型数据记录"` -} - -type TemplateDataLastInput struct { - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - Param map[string]interface{} `json:"param" dc:"搜索哪些字段的数据"` -} -type TemplateDataLastOutput struct { - Data g.Map `json:"data" dc:"模型数据记录"` -} - -// 数据模型设置主源、关联字段 -type TemplateDataRelationInput struct { - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - MainSourceId uint64 `json:"mainSourceId" dc:"主数据源" v:"required#主数据源ID不能为空"` - SourceNodeKey string `json:"sourceNodeKey" dc:"关联节点" v:"required#关联节点标识不能为空"` -} - -type DataTemplateDataInput struct { - Id uint64 `json:"id" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - Param map[string]interface{} `json:"param" dc:"搜索哪些字段的数据"` - PaginationInput -} -type DataTemplateDataOutput struct { - List string `json:"data" dc:"模型数据记录"` - PaginationOutput -} diff --git a/internal/model/data_template_busi.go b/internal/model/data_template_busi.go deleted file mode 100644 index 0dd8c8b..0000000 --- a/internal/model/data_template_busi.go +++ /dev/null @@ -1,7 +0,0 @@ -package model - -// 绑定业务模型 -type DataTemplateBusiAddInput struct { - DataTemplateId uint64 `json:"dataTemplateId" dc:"数据模型ID"` - BusiTypes []int `json:"busiTypes" dc:"业务单元"` -} diff --git a/internal/model/data_template_node.go b/internal/model/data_template_node.go deleted file mode 100644 index 68e29e5..0000000 --- a/internal/model/data_template_node.go +++ /dev/null @@ -1,63 +0,0 @@ -package model - -import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - - "github.com/gogf/gf/v2/util/gmeta" -) - -// 添加节点 -type DataTemplateNodeAddInput struct { - Tid uint64 `json:"tid" dc:"数据模型ID" v:"required#数据模型ID不能为空"` - From int `json:"from" dc:"字段生成方式:1=自动生成,2=数据源" v:"required|in:1,2#请选择字段生成方式|未知方式,请正确选择"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required-if:from,2#数据源ID不能为空"` - NodeId uint64 `json:"nodeId" dc:"数据节点ID" v:"required-if:from,2#数据节点ID不能为空"` - Key string `json:"key" dc:"模型节点标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入模型节点标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"模型节点名称" v:"required#请输入模型节点名称"` - DataType string `json:"dataType" dc:"数据类型" v:"required#请选择数据类型"` - Default string `json:"default" dc:"默认值"` - Method string `json:"method" dc:"数值类型,取值方式:max、min、avg"` - IsPk int `json:"isPk" dc:"是否主键"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - - IsSorting int `json:"isSorting" dc:"是否参与排序:0=否,1=是" v:"required|in:0,1#请选择是否参与排序|请正确选择"` - IsDesc int `json:"isDesc" dc:"排序方式:1=倒序,2=正序" v:"required-if:isSorting,1#请选择排序方式"` -} - -// 编辑节点 -type DataTemplateNodeEditInput struct { - Id uint64 `json:"id" dc:"模型节点ID" v:"required#模型节点ID不能为空"` - From int `json:"from" dc:"字段生成方式:1=自动生成,2=数据源" v:"required|in:1,2#请选择字段生成方式|未知方式,请正确选择"` - SourceId uint64 `json:"sourceId" dc:"数据源ID" v:"required-if:from,2#数据源ID不能为空"` - NodeId uint64 `json:"nodeId" dc:"数据节点ID" v:"required-if:from,2#数据节点ID不能为空"` - Name string `json:"name" dc:"模型节点名称" v:"required#请输入模型节点名称"` - Default string `json:"default" dc:"默认值"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - - IsSorting int `json:"isSorting" dc:"是否参与排序:0=否,1=是" v:"required|in:0,1#请选择是否参与排序|请正确选择"` - IsDesc int `json:"isDesc" dc:"排序方式:1=倒序,2=正序" v:"required-if:isSorting,1#请选择排序方式"` -} - -// 数据模型节点 -type DataTemplateNodeOutput struct { - *entity.DataTemplateNode - IsSorting int `json:"isSorting" dc:"是否参与排序:0=否,1=是"` - IsDesc int `json:"isDesc" dc:"排序方式:1=倒序,2=正序"` - - Source *WithSource `json:"source" orm:"with:source_id, where:source_id>0" dc:"数据源"` - Node *WithNode `json:"node" orm:"with:node_id, where:node_id>0" dc:"数据源节点"` -} - -type WithSource struct { - gmeta.Meta `orm:"table:data_source"` - SourceId uint64 `json:"sourceId" dc:"数据源ID"` - Key string `json:"key" dc:"数据源标识"` - Name string `json:"name" dc:"数据源名称"` - From int `json:"from" dc:"数据来源:1=api导入,2=数据库,3=文件,4=设备"` -} -type WithNode struct { - gmeta.Meta `orm:"table:data_node"` - NodeId uint64 `json:"nodeId" dc:"数据节点ID"` - Key string `json:"key" dc:"数据节点标识"` - Name string `json:"name" dc:"数据节点名称"` -} diff --git a/internal/model/dev_data_report.go b/internal/model/dev_data_report.go new file mode 100644 index 0000000..abe2328 --- /dev/null +++ b/internal/model/dev_data_report.go @@ -0,0 +1,28 @@ +package model + +// 上报属性数据 +type ReportPropertyData map[string]ReportPropertyNode + +// 属性值 +type ReportPropertyNode struct { + Value any // 属性值 + CreateTime int64 // 上报时间 +} + +// 上报事件数据 +type ReportEventData struct { + Key string // 事件标识 + Param ReportEventParam // 事件输出参数 +} + +// 事件输出参数 +type ReportEventParam struct { + Value map[string]any // 事件输出参数 + CreateTime int64 // 上报时间 +} + +// 设备上下线状态 +type ReportStatusData struct { + Status string // 状态:online、offline + CreateTime int64 // 上下线时间 +} diff --git a/internal/model/dev_device.go b/internal/model/dev_device.go index 5d32236..765001b 100644 --- a/internal/model/dev_device.go +++ b/internal/model/dev_device.go @@ -1,9 +1,10 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "sagooiot/internal/model/entity" "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/util/gmeta" ) @@ -15,11 +16,11 @@ const ( ) type DeviceInput struct { - Key string `json:"key" dc:"设备标识" v:"regex:^[A-Za-z_]+[\\w]*$#请输入设备标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"设备名称"` - ProductId uint `json:"productId" dc:"所属产品"` - TunnelId int `json:"tunnelId" description:"tunnelId"` - Status string `p:"status"` //设备状态 + Key string `json:"key" dc:"设备标识"` + Name string `json:"name" dc:"设备名称"` + ProductKey string `json:"productKey" dc:"所属产品"` + TunnelId int `json:"tunnelId" description:"tunnelId"` + Status string `p:"status"` //设备状态 } type DevDevice struct { @@ -34,16 +35,18 @@ type DevProductWithName struct { Key string `json:"key" dc:"产品标识"` } type DevProduct struct { - Id uint `json:"id" dc:"产品ID"` - Name string `json:"name" dc:"产品名称"` - Key string `json:"key" dc:"产品标识"` - Metadata string `json:"metadata" dc:"物模型"` + Id uint `json:"id" dc:"产品ID"` + Name string `json:"name" dc:"产品名称"` + Key string `json:"key" dc:"产品标识"` + Metadata string `json:"metadata" dc:"物模型"` + DeviceType string `json:"deviceType" dc:"设备类型"` } type DevDeviceTag struct { - Id uint `json:"id" dc:"标签ID"` - Key string `json:"key" dc:"标签标识"` - Name string `json:"name" dc:"标签名称"` - Value string `json:"value" dc:"标签值"` + Id uint `json:"id" dc:"标签ID"` + DeviceId uint `json:"deviceId" dc:"设备ID"` + Key string `json:"key" dc:"标签标识"` + Name string `json:"name" dc:"标签名称"` + Value string `json:"value" dc:"标签值"` } type DeviceOutput struct { @@ -51,31 +54,74 @@ type DeviceOutput struct { ProductName string `json:"productName" dc:"产品名称"` TSL *TSL `json:"tsl" dc:"物模型"` - Product *DevProduct `json:"product" orm:"with:id=product_id" dc:"产品信息"` - Tags []*DevDeviceTag `json:"tags" orm:"with:device_id=id" dc:"设备标签"` + Product *entity.DevProduct `json:"product" orm:"with:key=product_key" dc:"产品信息"` + Tags []*DevDeviceTag `json:"tags" orm:"with:device_id=id" dc:"设备标签"` } type AddDeviceInput struct { - Key string `json:"key" dc:"设备标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入设备标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"设备名称" v:"required#请输入设备名称"` - ProductId uint `json:"productId" dc:"所属产品" v:"required#请选择所属产品"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Certificate string `json:"certificate" dc:"设备证书"` - SecureKey string `json:"secureKey" dc:"设备密钥"` - Version string `json:"version" dc:"固件版本号"` + Key string `json:"key" dc:"设备标识" v:"required#请输入设备标识"` + Name string `json:"name" dc:"设备名称" v:"required#请输入设备名称"` + ProductKey string `json:"productKey" dc:"所属产品" v:"required#请选择所属产品"` + Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` + Version string `json:"version" dc:"固件版本号"` + Lng string `json:"lng" dc:"经度"` + Lat string `json:"lat" dc:"纬度"` + OnlineTimeout int `json:"online_timeout" dc:"设备超时时间,单位秒"` + + Tags []AddTagInput `json:"tags" dc:"设备标签"` + + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + + ExtensionInfo string `json:"extensionInfo" dc:"设备扩展信息"` + + Address string `json:"address" dc:"详细地址"` } type EditDeviceInput struct { - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` - Name string `json:"name" dc:"设备名称" v:"required#请输入设备名称"` - Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` - Certificate string `json:"certificate" dc:"设备证书"` - SecureKey string `json:"secureKey" dc:"设备密钥"` - Version string `json:"version" dc:"固件版本号"` + Key string `json:"key" dc:"设备标识" v:"required#请输入设备标识"` + Name string `json:"name" dc:"设备名称" v:"required#请输入设备名称"` + Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` + Version string `json:"version" dc:"固件版本号"` + Lng string `json:"lng" dc:"经度"` + Lat string `json:"lat" dc:"纬度"` + Tags []AddTagInput `json:"tags" dc:"设备标签"` + + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + + ExtensionInfo string `json:"extensionInfo" dc:"设备扩展信息"` + + Address string `json:"address" dc:"详细地址"` +} + +type DeviceExtendInput struct { + Key string `json:"key" dc:"设备标识" v:"required#请输入设备标识"` + OnlineTimeout int `json:"onlineTimeout" dc:"设备在线超时设置,单位:秒"` + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` } type ListDeviceInput struct { - ProductId uint `json:"productId" dc:"产品ID"` + ProductKey string `json:"productKey" dc:"产品ID"` +} + +type ListForSubInput struct { + ProductKey string `json:"productKey" dc:"产品标识" v:"required#产品KEY不能为空"` + GatewayKey string `json:"gatewayKey" dc:"网关标识"` + PaginationInput } type ListDeviceForPageInput struct { @@ -103,18 +149,31 @@ type DevicePropertiy struct { List []*gvar.Var `json:"list" dc:"当天属性值列表"` } +type DeviceLatestProperty struct { + Key string `json:"key" dc:"属性标识"` + Name string `json:"name" dc:"属性名称"` + Type string `json:"type" dc:"属性值类型"` + Unit string `json:"unit" dc:"属性值单位"` + Value *gvar.Var `json:"value" dc:"属性值"` +} + +type DevicePropertiyRes struct { + Ts *gtime.Time `json:"ts" dc:"时间"` + Value *gvar.Var `json:"value" dc:"属性值"` +} + type DevicePropertiyOut struct { Ts *gtime.Time `json:"ts" dc:"时间"` Value *gvar.Var `json:"value" dc:"属性值"` } type DeviceGetPropertyInput struct { - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + DeviceKey string `json:"device_key" dc:"设备ID" v:"required#设备key不能为空"` PropertyKey string `json:"propertyKey" dc:"属性标识" v:"required#属性标识不能为空"` } type DeviceGetPropertyListInput struct { - Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + DeviceKey string `json:"device_key" dc:"设备ID" v:"required#设备key不能为空"` PropertyKey string `json:"propertyKey" dc:"属性标识" v:"required#属性标识不能为空"` PaginationInput } @@ -123,16 +182,85 @@ type DeviceGetPropertyListOutput struct { PaginationOutput } +type DeviceGetDataInput struct { + DeviceKey string `json:"device_key" dc:"设备ID" v:"required#设备key不能为空"` + PropertyKey string `json:"propertyKey" dc:"属性标识" v:"required#属性标识不能为空"` + DateRange []string `json:"dateRange" dc:"日期范围"` + IsDesc int `json:"isDesc" dc:"排序:0=顺序,1=倒序"` +} + type DeviceTotalOutput struct { DeviceTotal int `json:"deviceTotal" dc:"设备总量"` DeviceOffline int `json:"deviceOffline" dc:"离线设备数量"` - ProductTotal int `json:"productTotal" dc:"产品总量"` - ProductAdded int `json:"productAdded" dc:"今日产品增量"` + ProductTotal int `json:"productTotal" dc:"产品总量"` + ProductAdded int `json:"productAdded" dc:"今日产品增量"` + ProductActivation int `json:"productActivation" dc:"产品启用"` + ProductDeactivation int `json:"productDeactivation" dc:"产品停用"` - MsgTotal int `json:"msgTotal" dc:"设备消息总量"` - MsgAdded int `json:"msgAdded" dc:"今日设备消息增量"` + MsgTotal int64 `json:"msgTotal" dc:"设备消息总量"` + MsgAdded int64 `json:"msgAdded" dc:"今日设备消息增量"` AlarmTotal int `json:"alarmTotal" dc:"设备报警总量"` AlarmAdded int `json:"alarmAdded" dc:"今日设备报警增量"` } + +type DeviceBindInput struct { + GatewayKey string `json:"gatewayKey" dc:"网关标识" v:"required#网关标识不能为空"` + SubKeys []string `json:"subKeys" dc:"子设备标识列表"` +} + +type DeviceBindListInput struct { + GatewayKey string `json:"gatewayKey" dc:"网关标识" v:"required#网关标识不能为空"` + PaginationInput +} + +type DeviceBindListOutput struct { + List []*DeviceOutput `json:"list" dc:"网关绑定子设备列表"` + PaginationOutput +} + +type CheckBindInput struct { + GatewayKey string `json:"gatewayKey" dc:"网关标识" v:"required#网关标识不能为空"` + SubKey string `json:"subKey" dc:"子设备标识" v:"required#子设备标识不能为空"` +} + +type AuthInfoInput struct { + DeviceKey string `json:"deviceKey" dc:"设备标识"` + ProductKey string `json:"ProductKey" dc:"产品标识"` +} +type AuthInfoOutput struct { + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Certificate *entity.SysCertificate `json:"certificate" dc:"证书信息"` +} +type DeviceExport struct { + ProductName string `json:"productName" dc:"产品名称"` + DeviceName string `json:"deviceName" dc:"设备名称"` + DeviceKey string `json:"deviceKey" dc:"设备标识"` + DeviceType string `json:"deviceType" dc:"设备类型"` + Status string `json:"status" dc:"状态"` + Desc string `json:"desc" dc:"说明" v:"max-length:200#描述长度不能超过200个字符"` + Version string `json:"version" dc:"固件版本号"` +} + +type DeviceDataListInput struct { + DeviceKey string `json:"deviceKey" dc:"设备标识" v:"required#请输入设备标识"` + Interval uint `json:"interval" dc:"聚合时间间隔" v:"required|min:1#请输入时间间隔"` + TimeUnit uint `json:"timeUnit" dc:"时间单位: 1=秒,2=分,3=小时,4=天" v:"in:1,2,3,4#时间单位不正确"` + + PaginationInput +} +type DeviceDataListOutput struct { + List gdb.Result `json:"list" dc:"设备数据列表"` + + PaginationOutput +} + +type DeviceExtensionInfoInput struct { + Id uint `json:"id" dc:"设备ID" v:"required#设备ID不能为空"` + ExtensionInfo string `json:"extensionInfo" dc:"设备扩展信息"` +} diff --git a/internal/model/dev_device_function.go b/internal/model/dev_device_function.go new file mode 100644 index 0000000..c9211a9 --- /dev/null +++ b/internal/model/dev_device_function.go @@ -0,0 +1,10 @@ +package model + +type DeviceFunctionInput struct { + DeviceKey string `json:"deviceKey" dc:"设备标识" v:"required#设备标识不能为空"` + FuncKey string `json:"funcKey" dc:"功能标识" v:"required#功能标识不能为空"` + Params map[string]any `json:"params" dc:"功能输入参数"` +} +type DeviceFunctionOutput struct { + Data map[string]any `json:"data" dc:"功能输出"` +} diff --git a/internal/model/dev_device_property.go b/internal/model/dev_device_property.go new file mode 100644 index 0000000..5178c61 --- /dev/null +++ b/internal/model/dev_device_property.go @@ -0,0 +1,9 @@ +package model + +type DevicePropertyInput struct { + DeviceKey string `json:"deviceKey" dc:"设备标识" v:"required#设备标识不能为空"` + Params map[string]any `json:"params" dc:"设备属性设置"` +} +type DevicePropertyOutput struct { + Data map[string]any `json:"data" dc:"设备属性设置输出"` +} diff --git a/internal/model/dev_device_tag.go b/internal/model/dev_device_tag.go index 1121b89..6d0c0f1 100644 --- a/internal/model/dev_device_tag.go +++ b/internal/model/dev_device_tag.go @@ -2,7 +2,7 @@ package model type AddTagDeviceInput struct { DeviceId uint `json:"deviceId" dc:"设备ID" v:"required#设备ID不能为空"` - DeviceKey string `json:"deviceKey" dc:"设备标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入设备标识|标识由字母、数字和下划线组成,且不能以数字开头"` + DeviceKey string `json:"deviceKey" dc:"设备标识" v:"required#设备标识不能为空"` Key string `json:"key" dc:"标签标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入标签标识|标识由字母、数字和下划线组成,且不能以数字开头"` Name string `json:"name" dc:"标签名称" v:"required#请输入标签名称"` Value string `json:"value" dc:"标签值" v:"required#请输入标签值"` @@ -13,3 +13,9 @@ type EditTagDeviceInput struct { Name string `json:"name" dc:"标签名称" v:"required#请输入标签名称"` Value string `json:"value" dc:"标签值" v:"required#请输入标签值"` } + +type AddTagInput struct { + Key string `json:"key" dc:"标签标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入标签标识|标识由字母、数字和下划线组成,且不能以数字开头"` + Name string `json:"name" dc:"标签名称" v:"required#请输入标签名称"` + Value string `json:"value" dc:"标签值" v:"required#请输入标签值"` +} diff --git a/internal/model/dev_device_tree.go b/internal/model/dev_device_tree.go new file mode 100644 index 0000000..7bbb8a4 --- /dev/null +++ b/internal/model/dev_device_tree.go @@ -0,0 +1,68 @@ +package model + +import ( + "sagooiot/internal/model/entity" + + "github.com/gogf/gf/v2/os/gtime" +) + +type AddDeviceTreeInfoInput struct { + Name string `json:"name" dc:"名称" v:"required#请输入名称"` + Address string `json:"address" dc:"地址" v:"required#请输入地址"` + Lng string `json:"lng" dc:"经度"` + Lat string `json:"lat" dc:"纬度"` + Contact string `json:"contact" dc:"联系人"` + Phone string `json:"phone" dc:"联系电话"` + StartDate *gtime.Time `json:"startDate" dc:"服务周期:开始日期"` + EndDate *gtime.Time `json:"endDate" dc:"服务周期:截止日期"` + Image string `json:"image" dc:"图片"` + DeviceKey string `json:"deviceKey" dc:"设备标识"` + Area string `json:"area" dc:"区域"` + Company string `json:"company" dc:"所属公司"` + ParentId int `json:"parentId" dc:"父ID"` + Duration int `json:"duration" dc:"时间窗口值"` + TimeUnit int `json:"timeUnit" dc:"时间单位:1=秒,2=分钟,3=小时,4=天"` + Template string `json:"template" dc:"页面模板,默认:default" d:"default"` + Category string `json:"category" dc:"分类"` + Types string `json:"types" dc:"类型"` +} + +type EditDeviceTreeInfoInput struct { + Id int `json:"id" dc:"信息ID" v:"required#信息ID不能为空"` + Name string `json:"name" dc:"名称" v:"required#请输入名称"` + Address string `json:"address" dc:"地址" v:"required#请输入地址"` + Lng string `json:"lng" dc:"经度"` + Lat string `json:"lat" dc:"纬度"` + Contact string `json:"contact" dc:"联系人"` + Phone string `json:"phone" dc:"联系电话"` + StartDate *gtime.Time `json:"startDate" dc:"服务周期:开始日期"` + EndDate *gtime.Time `json:"endDate" dc:"服务周期:截止日期"` + Image string `json:"image" dc:"图片"` + DeviceKey string `json:"deviceKey" dc:"设备标识"` + Area string `json:"area" dc:"区域"` + Company string `json:"company" dc:"所属公司"` + ParentId int `json:"parentId" dc:"父ID"` + Duration int `json:"duration" dc:"时间窗口值"` + TimeUnit int `json:"timeUnit" dc:"时间单位:1=秒,2=分钟,3=小时,4=天"` + Template string `json:"template" dc:"页面模板,默认:default" d:"default"` + Category string `json:"category" dc:"分类"` + Types string `json:"types" dc:"类型"` +} + +type DetailDeviceTreeInfoOutput struct { + entity.DevDeviceTreeInfo + ParentId int `json:"parentId" dc:"父信息ID"` +} + +type ( + DeviceTreeListOutput struct { + *DeviceTree + Children []*DeviceTreeListOutput `json:"children" dc:"子集"` + } + DeviceTree struct { + Id int `json:"id" dc:"设备树ID"` + InfoId int `json:"infoId" dc:"信息ID"` + ParentInfoId int `json:"parentInfoId" dc:"父信息ID"` + Name string `json:"name" dc:"名称"` + } +) diff --git a/internal/model/dev_product.go b/internal/model/dev_product.go index 8dca835..c0be52a 100644 --- a/internal/model/dev_product.go +++ b/internal/model/dev_product.go @@ -1,7 +1,7 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "sagooiot/internal/model/entity" ) const ( @@ -9,6 +9,12 @@ const ( ProductStatusOn // 产品已发布 ) +const ( + DeviceTypeDefault string = "设备" + DeviceTypeGateway string = "网关" + DeviceTypeSub string = "子设备" +) + type ListForPageInput struct { ProductInput PaginationInput @@ -22,7 +28,7 @@ type ProductInput struct { Name string `json:"name" dc:"产品名称" ` CategoryId uint `json:"categoryId" dc:"所属品类"` MessageProtocols []string `json:"messageProtocol" dc:"消息协议"` - DeviceTypes []string `json:"deviceType" dc:"设备类型:网关、设备"` + DeviceTypes []string `json:"deviceType" dc:"设备类型:网关、设备、子设备"` Status string `p:"status"` //产品状态 } @@ -54,23 +60,68 @@ type DetailProductOutput struct { } type AddProductInput struct { - Key string `json:"key" dc:"产品标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入产品标识|标识由字母、数字和下划线组成,且不能以数字开头"` + Key string `json:"key" dc:"产品标识" v:"required#请输入产品标识"` Name string `json:"name" dc:"产品名称" v:"required#请输入产品名称"` CategoryId uint `json:"categoryId" dc:"所属品类" v:"required#请选择所属品类"` MessageProtocol string `json:"messageProtocol" dc:"消息协议" v:"required#请选择消息协议"` TransportProtocol string `json:"transportProtocol" dc:"传输协议: MQTT,COAP,UDP" v:"required#请选择传输协议"` - DeviceType string `json:"deviceType" dc:"设备类型:网关、设备" v:"required#请选择设备类型"` + DeviceType string `json:"deviceType" dc:"设备类型:网关、设备、子设备" v:"required#请选择设备类型"` Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` Icon string `json:"icon" dc:"图片地址"` + ScriptInfo string `json:"scriptInfo" dc:"脚本信息"` + + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` } type EditProductInput struct { - Id uint `json:"id" dc:"产品ID" v:"required#产品ID不能为空"` + Key string `json:"key" dc:"产品标识" v:"required#请输入产品标识"` Name string `json:"name" dc:"产品名称" v:"required#请输入产品名称"` CategoryId uint `json:"categoryId" dc:"所属品类" v:"required#请选择所属品类"` MessageProtocol string `json:"messageProtocol" dc:"消息协议" v:"required#请选择消息协议"` TransportProtocol string `json:"transportProtocol" dc:"传输协议: MQTT,COAP,UDP" v:"required#请选择传输协议"` - DeviceType string `json:"deviceType" dc:"设备类型:网关、设备" v:"required#请选择设备类型"` + DeviceType string `json:"deviceType" dc:"设备类型:网关、设备、子设备" v:"required#请选择设备类型"` Desc string `json:"desc" dc:"描述" v:"max-length:200#描述长度不能超过200个字符"` Icon *string `json:"icon" dc:"图片地址"` + ScriptInfo string `json:"scriptInfo" dc:"脚本信息"` + + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` +} + +type ExtendInput struct { + Key string `json:"key" dc:"产品标识" v:"required#请输入产品标识"` + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` +} + +type ScriptInfoInput struct { + Key string `json:"key" dc:"产品标识" v:"required#请输入产品标识"` + ScriptInfo string `json:"scriptInfo" description:"脚本信息"` +} + +type DeviceConnectIntroOutput struct { + Name string `json:"name" dc:"接入方式"` + Protocol string `json:"protocol" dc:"消息协议"` + Description string `json:"description" dc:"描述"` + Link string `json:"link" dc:"连接信息"` + // 认证信息 + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + CertificateName string `json:"certificateName" dc:"证书名称"` } diff --git a/internal/model/dev_product_category.go b/internal/model/dev_product_category.go index 4a9d6b0..3e76edc 100644 --- a/internal/model/dev_product_category.go +++ b/internal/model/dev_product_category.go @@ -1,6 +1,6 @@ package model -import "github.com/sagoo-cloud/sagooiot/internal/model/entity" +import "sagooiot/internal/model/entity" type ProductCategoryTreeOutput struct { *entity.DevProductCategory @@ -16,11 +16,14 @@ type AddProductCategoryInput struct { Key string `json:"key" description:"分类标识" v:"required#请输入标识"` Name string `json:"name" description:"分类名称" v:"required#请输入名称"` Desc string `json:"desc" description:"描述" v:"max-length:200#描述长度不能超过200个字符"` + Sort int `json:"sort" dc:"排序"` } type EditProductCategoryInput struct { - Id uint `json:"id" description:"分类ID" v:"required#分类ID不能为空"` - Key string `json:"key" description:"分类标识" v:"required#请输入标识"` - Name string `json:"name" description:"分类名称" v:"required#请输入名称"` - Desc string `json:"desc" description:"描述" v:"max-length:200#描述长度不能超过200个字符"` + Id uint `json:"id" description:"分类ID" v:"required#分类ID不能为空"` + ParentId uint `json:"parentId" description:"父级分类ID"` + Key string `json:"key" description:"分类标识" v:"required#请输入标识"` + Name string `json:"name" description:"分类名称" v:"required#请输入名称"` + Desc string `json:"desc" description:"描述" v:"max-length:200#描述长度不能超过200个字符"` + Sort int `json:"sort" dc:"排序"` } diff --git a/internal/model/dev_protocol.go b/internal/model/dev_protocol.go deleted file mode 100644 index 548edd8..0000000 --- a/internal/model/dev_protocol.go +++ /dev/null @@ -1,13 +0,0 @@ -package model - -// 消息协议 -type MessageProtocolRes struct { - Key string `json:"key" dc:"协议标识"` - Name string `json:"name" dc:"协议名称"` -} - -// 传输协议 -type TrunsportProtocolRes struct { - Key string `json:"key" dc:"协议标识"` - Name string `json:"name" dc:"协议名称"` -} diff --git a/internal/model/dev_tsl.go b/internal/model/dev_tsl.go index 41b2856..8eb61cd 100644 --- a/internal/model/dev_tsl.go +++ b/internal/model/dev_tsl.go @@ -48,9 +48,9 @@ type TSLArrayType struct { // 扩展类型参数:对象型 type TSLObjectType struct { Key string `json:"key" dc:"参数标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"参数名称"` // 参数名称 - ValueType TSLValueType `json:"valueType" dc:"参数值"` // 参数值 - Desc string `json:"desc" dc:"描述"` // 描述 + Name string `json:"name" dc:"参数名称"` + ValueType TSLValueType `json:"valueType" dc:"参数值"` + Desc string `json:"desc" dc:"描述"` } // 类型参数 @@ -62,53 +62,69 @@ type TSLParam struct { // 属性 type TSLProperty struct { Key string `json:"key" dc:"属性标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入属性标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"属性名称" v:"required#请输入属性名称"` // 属性名称 - AccessMode int `json:"accessMode" dc:"属性访问类型:0=读写,1=只读" v:"required#请选择是否只读"` // 属性访问类型 - ValueType TSLValueType `json:"valueType" dc:"属性值"` // 属性值 - Desc string `json:"desc" dc:"描述"` // 描述 + Name string `json:"name" dc:"属性名称" v:"required#请输入属性名称"` + AccessMode int `json:"accessMode" dc:"属性访问类型:0=读写,1=只读" v:"required#请选择是否只读"` + ValueType TSLValueType `json:"valueType" dc:"属性值"` + Desc string `json:"desc" dc:"描述"` } // 功能 type TSLFunction struct { - Key string `json:"key" dc:"功能标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入功能标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"功能名称" v:"required#请输入功能名称"` // 功能名称 - Inputs []TSLFunctionInput `json:"inputs" dc:"输入参数"` // 输入参数 - Output TSLValueType `json:"output" dc:"输出参数"` // 输出参数 - Desc string `json:"desc" dc:"描述"` // 描述 + Key string `json:"key" dc:"功能标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入功能标识|标识由字母、数字和下划线组成,且不能以数字开头"` + Name string `json:"name" dc:"功能名称" v:"required#请输入功能名称"` + Inputs []TSLFunctionInput `json:"inputs" dc:"输入参数"` + Outputs []TSLFunctionOutput `json:"outputs" dc:"输出参数"` + Desc string `json:"desc" dc:"描述"` } // 功能:输入参数 type TSLFunctionInput struct { Key string `json:"key" dc:"参数标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"参数名称"` // 输入参数名称 - ValueType TSLValueType `json:"valueType" dc:"参数值"` // 参数值 - Desc string `json:"desc" dc:"描述"` // 描述 + Name string `json:"name" dc:"参数名称"` + ValueType TSLValueType `json:"valueType" dc:"参数值"` + Desc string `json:"desc" dc:"描述"` +} + +// 功能:输出参数 +type TSLFunctionOutput struct { + Key string `json:"key" dc:"参数标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` + Name string `json:"name" dc:"参数名称"` + ValueType TSLValueType `json:"valueType" dc:"参数值"` + Desc string `json:"desc" dc:"描述"` } // 事件 type TSLEvent struct { - Key string `json:"key" dc:"事件标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入事件标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"事件名称" v:"required#请输入事件名称"` // 事件名称 - Level int `json:"level" dc:"事件级别:0=普通,1=警告,2=紧急" v:"required#请选择事件级别"` // 事件级别 - ValueType TSLValueType `json:"valueType" dc:"事件值"` // 事件值 - Desc string `json:"desc" dc:"描述"` // 描述 + Key string `json:"key" dc:"事件标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入事件标识|标识由字母、数字和下划线组成,且不能以数字开头"` + Name string `json:"name" dc:"事件名称" v:"required#请输入事件名称"` + Level int `json:"level" dc:"事件级别:0=普通,1=警告,2=紧急" v:"required#请选择事件级别"` + Outputs []TSLEventOutput `json:"outputs" dc:"输出参数"` + Desc string `json:"desc" dc:"描述"` +} + +// 事件:输入参数 +type TSLEventOutput struct { + Key string `json:"key" dc:"参数标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` + Name string `json:"name" dc:"参数名称"` + ValueType TSLValueType `json:"valueType" dc:"参数值"` + Desc string `json:"desc" dc:"描述"` } // 标签 type TSLTag struct { Key string `json:"key" dc:"标签标识" v:"required|regex:^[A-Za-z_]+[\\w]*$#请输入标签标识|标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"标签名称" v:"required#请输入标签名称"` // 标签名称 - AccessMode int `json:"accessMode" dc:"标签访问类型:0=读写,1=只读" v:"required#请选择是否只读"` // 标签访问类型 - ValueType TSLValueType `json:"valueType" dc:"标签值"` // 标签值 - Desc string `json:"desc" dc:"描述"` // 描述 + Name string `json:"name" dc:"标签名称" v:"required#请输入标签名称"` + AccessMode int `json:"accessMode" dc:"标签访问类型:0=读写,1=只读" v:"required#请选择是否只读"` + ValueType TSLValueType `json:"valueType" dc:"标签值"` + Desc string `json:"desc" dc:"描述"` } // 物模型 type TSL struct { - Key string `json:"key" dc:"产品标识" v:"regex:^[A-Za-z_]+[\\w]*$#标识由字母、数字和下划线组成,且不能以数字开头"` - Name string `json:"name" dc:"产品名称"` // 产品名称 - Properties []TSLProperty `json:"properties" dc:"属性"` // 属性 - Functions []TSLFunction `json:"functions" dc:"功能"` // 功能 - Events []TSLEvent `json:"events" dc:"事件"` // 事件 - Tags []TSLTag `json:"tags" dc:"标签"` // 标签 + Key string `json:"key" dc:"产品标识"` + Name string `json:"name" dc:"产品名称"` + Properties []TSLProperty `json:"properties" dc:"属性"` + Functions []TSLFunction `json:"functions" dc:"功能"` + Events []TSLEvent `json:"events" dc:"事件"` + Tags []TSLTag `json:"tags" dc:"标签"` } diff --git a/internal/model/dev_tsl_data_type.go b/internal/model/dev_tsl_data_type.go index bb6d375..50bdad6 100644 --- a/internal/model/dev_tsl_data_type.go +++ b/internal/model/dev_tsl_data_type.go @@ -1,7 +1,8 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/consts" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" "strconv" "time" ) @@ -27,7 +28,7 @@ type DataTypeValueExtension struct { // 参数值(类型、类型参数) type TSLValueType struct { - Type string `json:"type" dc:"数据类型" v:"required#请选择数据类型"` // 类型 + Type string `json:"type" ` // 类型 TSLParam // 参数 } @@ -42,12 +43,14 @@ func (t TSLValueType) ConvertValue(v interface{}) interface{} { transfer = TFloat(t.TSLParam) case consts.TypeDouble: transfer = TDouble(t.TSLParam) - case consts.TypeText: + case consts.TypeText, consts.TypeString: transfer = TText(t.TSLParam) case consts.TypeBool: transfer = TBoolean(t.TSLParam) case consts.TypeDate: transfer = TDate(t.TSLParam) + case consts.TypeTimestamp: + transfer = TTimestamp(t.TSLParam) case consts.TypeEnum: transfer = TEnum(t.TSLParam) case consts.TypeArray: @@ -67,18 +70,11 @@ type Transfer interface { type TInt TSLParam func (tInt TInt) Convert(v interface{}) interface{} { - number, ok := v.(int) - if !ok { - floatNumber, floatNumberOk := v.(float64) - if !floatNumberOk { - return 0 - } - number = int(floatNumber) - } + number := gconv.Int(v) if tInt.TSLParamBase.Min != nil && *tInt.TSLParamBase.Min > number { return *tInt.TSLParamBase.Min } - if tInt.TSLParamBase.Max != nil && *tInt.TSLParamBase.Max > number { + if tInt.TSLParamBase.Max != nil && *tInt.TSLParamBase.Max < number { return *tInt.TSLParamBase.Max } return number @@ -87,14 +83,11 @@ func (tInt TInt) Convert(v interface{}) interface{} { type TLong TSLParam func (tLong TLong) Convert(v interface{}) interface{} { - number, ok := v.(float64) - if !ok { - return 0 - } - if tLong.TSLParamBase.Min != nil && float64(*tLong.TSLParamBase.Min) > number { + number := gconv.Int64(v) + if tLong.TSLParamBase.Min != nil && int64(*tLong.TSLParamBase.Min) > number { return *tLong.TSLParamBase.Min } - if tLong.TSLParamBase.Max != nil && float64(*tLong.TSLParamBase.Max) > number { + if tLong.TSLParamBase.Max != nil && int64(*tLong.TSLParamBase.Max) > number { return *tLong.TSLParamBase.Max } return number @@ -103,14 +96,11 @@ func (tLong TLong) Convert(v interface{}) interface{} { type TFloat TSLParam func (tFloat TFloat) Convert(v interface{}) interface{} { - number, ok := v.(float64) - if !ok { - return 0 - } + number := gconv.Float64(v) if tFloat.TSLParamBase.Min != nil && float32(*tFloat.TSLParamBase.Min) > float32(number) { number = float64(*tFloat.TSLParamBase.Min) } - if tFloat.TSLParamBase.Max != nil && float32(*tFloat.TSLParamBase.Max) > float32(number) { + if tFloat.TSLParamBase.Max != nil && float32(*tFloat.TSLParamBase.Max) < float32(number) { number = float64(*tFloat.TSLParamBase.Max) } defaultDecimal := 2 @@ -124,10 +114,7 @@ func (tFloat TFloat) Convert(v interface{}) interface{} { type TDouble TSLParam func (tDouble TDouble) Convert(v interface{}) interface{} { - number, ok := v.(float64) - if !ok { - return 0 - } + number := gconv.Float64(v) if tDouble.TSLParamBase.Min != nil && float64(*tDouble.TSLParamBase.Min) > number { number = float64(*tDouble.TSLParamBase.Min) } @@ -145,10 +132,7 @@ func (tDouble TDouble) Convert(v interface{}) interface{} { type TText TSLParam func (tText TText) Convert(v interface{}) interface{} { - text, ok := v.(string) - if !ok { - return "" - } + text := gconv.String(v) if tText.MaxLength != nil && *tText.MaxLength > 0 && len(text) > *tText.MaxLength { return text[:*tText.MaxLength-1] } else { @@ -159,48 +143,53 @@ func (tText TText) Convert(v interface{}) interface{} { type TBoolean TSLParam func (tBoolean TBoolean) Convert(v interface{}) interface{} { - b, ok := v.(bool) - if !ok { - return "" + b := gconv.Bool(v) + if tBoolean.TSLParamBase.TrueValue != nil && *tBoolean.TSLParamBase.TrueValue == b { + return true } - //TODO 这里是返回文字还是类型,暂定是返回映射文字 - if !b && tBoolean.FalseText != nil { - return *tBoolean.FalseText - } else if b && tBoolean.TrueText != nil { - return *tBoolean.TrueText + if tBoolean.TSLParamBase.FalseValue != nil && *tBoolean.TSLParamBase.FalseValue == b { + return false } - return "" + return b } type TDate TSLParam -const layout = "2006-01-02 15:04:05" +const ( + layoutWithMill = "2006-01-02 15:04:05.000" + layoutWithSecond = "2006-01-02 15:04:05" +) -func (TDate TDate) Convert(v interface{}) interface{} { - str, ok := v.(string) - if !ok { - return time.Time{} +func (tDate TDate) Convert(v interface{}) interface{} { + str := gconv.String(v) + layout := layoutWithMill + if len(str) == len(layoutWithSecond) { + layout = layoutWithSecond } t, err := time.Parse(layout, str) if err != nil { - return time.Time{} + return time.Time{}.Format(layoutWithMill) } else { - return t + return t.Format(layoutWithMill) } } +type TTimestamp TSLParam + +func (tTimestamp TTimestamp) Convert(v interface{}) interface{} { + t := time.UnixMilli(gconv.Int64(v)) + return t.Format(layoutWithMill) +} + type TEnum TSLParam func (tEnum TEnum) Convert(v interface{}) interface{} { - tE, ok := v.(string) - if !ok { - return "" - } + tE := gconv.String(v) if tEnum.TSLParamExtension.Elements == nil { return "" } for _, node := range tEnum.TSLParamExtension.Elements { - if node.Text == tE { + if node.Value == tE { return node.Value } } @@ -236,8 +225,8 @@ func (tObject TObject) Convert(v interface{}) interface{} { if tObject.TSLParamExtension.Properties != nil { for k, value := range m { for _, t := range tObject.TSLParamExtension.Properties { - if t.Name == k { - result[t.Name] = t.ValueType.ConvertValue(value) + if t.Key == k { + result[t.Key] = t.ValueType.ConvertValue(value) } } } diff --git a/internal/model/dev_tsl_event.go b/internal/model/dev_tsl_event.go index 07568d0..a279c26 100644 --- a/internal/model/dev_tsl_event.go +++ b/internal/model/dev_tsl_event.go @@ -1,19 +1,19 @@ package model -// 事件:添加、编辑 -type TSLEventInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` +// TSLEventAddInput 事件:添加、编辑 +type TSLEventAddInput struct { + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` TSLEvent } -// 事件:删除 +// DelTSLEventInput 事件:删除 type DelTSLEventInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` - Key string `json:"key" dc:"事件标识" v:"required#事件标识不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Key string `json:"key" dc:"事件标识" v:"required#事件标识不能为空"` } type ListTSLEventInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` PaginationInput } type ListTSLEventOutput struct { diff --git a/internal/model/dev_tsl_function.go b/internal/model/dev_tsl_function.go index 8ae1e7b..2cff625 100644 --- a/internal/model/dev_tsl_function.go +++ b/internal/model/dev_tsl_function.go @@ -2,18 +2,18 @@ package model // 功能:添加、编辑 type TSLFunctionAddInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` TSLFunction } // 功能:删除 type DelTSLFunctionInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` - Key string `json:"key" dc:"功能标识" v:"required#功能标识不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Key string `json:"key" dc:"功能标识" v:"required#功能标识不能为空"` } type ListTSLFunctionInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` PaginationInput } type ListTSLFunctionOutput struct { diff --git a/internal/model/dev_tsl_property.go b/internal/model/dev_tsl_property.go index 2f6b254..8954e07 100644 --- a/internal/model/dev_tsl_property.go +++ b/internal/model/dev_tsl_property.go @@ -2,20 +2,20 @@ package model // 添加、编辑属性 type TSLPropertyInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` TSLProperty } // 删除属性 type DelTSLPropertyInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` - Key string `json:"key" dc:"属性标识" v:"required#属性标识不能为空"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Key string `json:"key" dc:"属性标识" v:"required#属性标识不能为空"` } type ListTSLPropertyInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` - Name string `json:"name" dc:"属性名称"` - DateType string `json:"dateType" dc:"数据类型"` + ProductKey string `json:"productKey" dc:"产品Key" v:"required#产品Key不能为空"` + Name string `json:"name" dc:"属性名称"` + DateType string `json:"dateType" dc:"数据类型"` PaginationInput } type ListTSLPropertyOutput struct { diff --git a/internal/model/dev_tsl_tag.go b/internal/model/dev_tsl_tag.go index 03ecf33..db2b96d 100644 --- a/internal/model/dev_tsl_tag.go +++ b/internal/model/dev_tsl_tag.go @@ -1,19 +1,19 @@ package model -// 添加、编辑标签 +// TSLTagInput 添加、编辑标签 type TSLTagInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品标识key" v:"required#产品KEY不能为空"` TSLTag } -// 删除标签 +// DelTSLTagInput 删除标签 type DelTSLTagInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` - Key string `json:"key" dc:"标签标识" v:"required#标签标识不能为空"` + ProductKey string `json:"productKey" dc:"产品标识key" v:"required#产品KEY不能为空"` + Key string `json:"key" dc:"标签标识" v:"required#标签标识不能为空"` } type ListTSLTagInput struct { - ProductId uint `json:"productId" dc:"产品ID" v:"required#产品ID不能为空"` + ProductKey string `json:"productKey" dc:"产品标识key" v:"required#产品KEY不能为空"` PaginationInput } type ListTSLTagOutput struct { diff --git a/internal/model/do/alarm_level.go b/internal/model/do/alarm_level.go index 395f05a..e1d8539 100644 --- a/internal/model/do/alarm_level.go +++ b/internal/model/do/alarm_level.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/alarm_log.go b/internal/model/do/alarm_log.go index 16bf389..70059d5 100644 --- a/internal/model/do/alarm_log.go +++ b/internal/model/do/alarm_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,16 +13,18 @@ import ( type AlarmLog struct { g.Meta `orm:"table:alarm_log, do:true"` Id interface{} // + DeptId interface{} // 部门ID Type interface{} // 告警类型:1=规则告警,2=设备自主告警 RuleId interface{} // 规则id RuleName interface{} // 规则名称 Level interface{} // 告警级别 Data interface{} // 触发告警的数据 + Expression interface{} // 触发告警的表达式 ProductKey interface{} // 产品标识 DeviceKey interface{} // 设备标识 Status interface{} // 告警状态:0=未处理,1=已处理 CreatedAt *gtime.Time // 告警时间 - UpdateBy interface{} // 告警处理人员 + UpdatedBy interface{} // 告警处理人员 UpdatedAt *gtime.Time // 处理时间 Content interface{} // 处理意见 } diff --git a/internal/model/do/alarm_rule.go b/internal/model/do/alarm_rule.go index a6843ac..63b516a 100644 --- a/internal/model/do/alarm_rule.go +++ b/internal/model/do/alarm_rule.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,16 +13,19 @@ import ( type AlarmRule struct { g.Meta `orm:"table:alarm_rule, do:true"` Id interface{} // + DeptId interface{} // 部门ID Name interface{} // 告警规则名称 Level interface{} // 告警级别,默认:4(一般) ProductKey interface{} // 产品标识 DeviceKey interface{} // 设备标识 - TriggerType interface{} // 触发类型:1=上线,2=离线,3=属性上报 + TriggerMode interface{} // 触发方式:1=设备触发,2=定时触发 + TriggerType interface{} // 触发类型:1=上线,2=离线,3=属性上报, 4=事件上报 + EventKey interface{} // 事件标识 TriggerCondition interface{} // 触发条件 Action interface{} // 执行动作 Status interface{} // 状态:0=未启用,1=已启用 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 DeletedBy interface{} // 删除者 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/base_db_link.go b/internal/model/do/base_db_link.go deleted file mode 100644 index f1f9929..0000000 --- a/internal/model/do/base_db_link.go +++ /dev/null @@ -1,31 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// BaseDbLink is the golang structure of table base_db_link for DAO operations like Where/Data. -type BaseDbLink struct { - g.Meta `orm:"table:base_db_link, do:true"` - Id interface{} // - Name interface{} // 名称 - Types interface{} // 驱动类型 mysql或oracle - Host interface{} // 主机地址 - Port interface{} // 端口号 - UserName interface{} // 用户名称 - Password interface{} // 密码 - Description interface{} // 描述 - Status interface{} // 状态 0 停用 1启用 - IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreatedBy interface{} // 创建人 - CreatedAt *gtime.Time // 创建时间 - UpdatedBy interface{} // 修改人 - UpdatedAt *gtime.Time // 更新时间 - DeletedBy interface{} // 删除人 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/casbin_rule.go b/internal/model/do/casbin_rule.go new file mode 100644 index 0000000..0cf6108 --- /dev/null +++ b/internal/model/do/casbin_rule.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// CasbinRule is the golang structure of table casbin_rule for DAO operations like Where/Data. +type CasbinRule struct { + g.Meta `orm:"table:casbin_rule, do:true"` + Ptype interface{} // + V0 interface{} // + V1 interface{} // + V2 interface{} // + V3 interface{} // + V4 interface{} // + V5 interface{} // +} diff --git a/internal/model/do/data_node.go b/internal/model/do/data_node.go deleted file mode 100644 index efb30eb..0000000 --- a/internal/model/do/data_node.go +++ /dev/null @@ -1,29 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// DataNode is the golang structure of table data_node for DAO operations like Where/Data. -type DataNode struct { - g.Meta `orm:"table:data_node, do:true"` - NodeId interface{} // - SourceId interface{} // 数据源ID - Name interface{} // 数据节点名称 - Key interface{} // 数据节点标识 - DataType interface{} // 数据类型 - Value interface{} // 取值项 - IsPk interface{} // 是否主键:0=否,1=是 - Rule interface{} // 规则配置json - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 - DeletedBy interface{} // 删除者 - CreatedAt *gtime.Time // 创建时间 - UpdatedAt *gtime.Time // 更新时间 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/data_source.go b/internal/model/do/data_source.go deleted file mode 100644 index fb0c3ad..0000000 --- a/internal/model/do/data_source.go +++ /dev/null @@ -1,31 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// DataSource is the golang structure of table data_source for DAO operations like Where/Data. -type DataSource struct { - g.Meta `orm:"table:data_source, do:true"` - SourceId interface{} // - Name interface{} // 数据源名称 - Key interface{} // 数据源标识 - Desc interface{} // 描述 - From interface{} // 数据来源:1=api导入,2=数据库,3=文件,4=设备 - Config interface{} // 数据源配置json:api配置、数据库配置、文件配置 - Rule interface{} // 规则配置json - LockKey interface{} // 锁定key标识:0=未锁定,1=锁定,不允许修改 - Status interface{} // 状态:0=未发布,1=已发布 - DataTable interface{} // 数据表名称 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 - DeletedBy interface{} // 删除者 - CreatedAt *gtime.Time // 创建时间 - UpdatedAt *gtime.Time // 更新时间 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/data_template.go b/internal/model/do/data_template.go deleted file mode 100644 index 0a12669..0000000 --- a/internal/model/do/data_template.go +++ /dev/null @@ -1,33 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplate is the golang structure of table data_template for DAO operations like Where/Data. -type DataTemplate struct { - g.Meta `orm:"table:data_template, do:true"` - Id interface{} // ID - Name interface{} // 名称 - Key interface{} // 标识 - Desc interface{} // 描述 - Status interface{} // 状态:0=未发布,1=已发布 - CronExpression interface{} // cron执行表达式 - SortNodeKey interface{} // 排序节点标识 - SortDesc interface{} // 排序方式:1=倒序,2=正序 - DataTable interface{} // 数据表名称 - LockKey interface{} // 锁定key标识:0=未锁定,1=锁定,不允许修改 - MainSourceId interface{} // 主数据源 - SourceNodeKey interface{} // 数据源关联节点 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 - DeletedBy interface{} // 删除者 - CreatedAt *gtime.Time // 创建时间 - UpdatedAt *gtime.Time // 更新时间 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/data_template_busi.go b/internal/model/do/data_template_busi.go deleted file mode 100644 index 61541a5..0000000 --- a/internal/model/do/data_template_busi.go +++ /dev/null @@ -1,23 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplateBusi is the golang structure of table data_template_busi for DAO operations like Where/Data. -type DataTemplateBusi struct { - g.Meta `orm:"table:data_template_busi, do:true"` - Id interface{} // - DataTemplateId interface{} // 数据建模ID - BusiTypes interface{} // 业务单元 - IsDeleted interface{} // 0未删除 1已删除 - CreatedBy interface{} // 创建人 - CreatedAt *gtime.Time // 创建时间 - DeletedBy interface{} // 删除人 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/data_template_node.go b/internal/model/do/data_template_node.go deleted file mode 100644 index 027b5a8..0000000 --- a/internal/model/do/data_template_node.go +++ /dev/null @@ -1,33 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package do - -import ( - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplateNode is the golang structure of table data_template_node for DAO operations like Where/Data. -type DataTemplateNode struct { - g.Meta `orm:"table:data_template_node, do:true"` - Id interface{} // ID - Tid interface{} // 模型ID - From interface{} // 字段生成方式:1=自动生成,2=数据源 - SourceId interface{} // 数据源ID - NodeId interface{} // 数据源ID - Name interface{} // 节点名称 - Key interface{} // 节点标识 - DataType interface{} // 数据类型 - Default interface{} // 默认值 - Method interface{} // 数值类型,取值方式 - IsPk interface{} // 是否主键:0=否,1=是 - Desc interface{} // 描述 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 - DeletedBy interface{} // 删除者 - CreatedAt *gtime.Time // 创建时间 - UpdatedAt *gtime.Time // 更新时间 - DeletedAt *gtime.Time // 删除时间 -} diff --git a/internal/model/do/dev_device.go b/internal/model/do/dev_device.go index 4cd4c26..ecf4422 100644 --- a/internal/model/do/dev_device.go +++ b/internal/model/do/dev_device.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,20 +13,27 @@ import ( type DevDevice struct { g.Meta `orm:"table:dev_device, do:true"` Id interface{} // + DeptId interface{} // 部门ID Key interface{} // 设备标识 Name interface{} // 设备名称 - ProductId interface{} // 所属产品 + ProductKey interface{} // 所属产品KEY Desc interface{} // 描述 MetadataTable interface{} // 是否生成物模型子表:0=否,1=是 Status interface{} // 状态:0=未启用,1=离线,2=在线 + OnlineTimeout interface{} // 设备在线超时设置,单位:秒 RegistryTime *gtime.Time // 激活时间 LastOnlineTime *gtime.Time // 最后上线时间 - Certificate interface{} // 设备证书 - SecureKey interface{} // 设备密钥 Version interface{} // 固件版本号 TunnelId interface{} // tunnelId - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + Lng interface{} // 经度 + Lat interface{} // 纬度 + AuthType interface{} // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser interface{} // 认证用户 + AuthPasswd interface{} // 认证密码 + AccessToken interface{} // AccessToken + CertificateId interface{} // 证书ID + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 DeletedBy interface{} // 删除者 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/dev_device_gateway.go b/internal/model/do/dev_device_gateway.go new file mode 100644 index 0000000..f6965ed --- /dev/null +++ b/internal/model/do/dev_device_gateway.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// DevDeviceGateway is the golang structure of table dev_device_gateway for DAO operations like Where/Data. +type DevDeviceGateway struct { + g.Meta `orm:"table:dev_device_gateway, do:true"` + Id interface{} // + GatewayKey interface{} // 网关标识 + SubKey interface{} // 子设备标识 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 + DeletedBy interface{} // 删除者 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 + DeletedAt *gtime.Time // 删除时间 +} diff --git a/internal/model/do/dev_device_tag.go b/internal/model/do/dev_device_tag.go index 26f7a63..9ef08ee 100644 --- a/internal/model/do/dev_device_tag.go +++ b/internal/model/do/dev_device_tag.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,13 +13,14 @@ import ( type DevDeviceTag struct { g.Meta `orm:"table:dev_device_tag, do:true"` Id interface{} // + DeptId interface{} // 部门ID DeviceId interface{} // 设备ID DeviceKey interface{} // 设备标识 Key interface{} // 标签标识 Name interface{} // 标签名称 Value interface{} // 标签值 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 DeletedBy interface{} // 删除者 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/dev_device_tree.go b/internal/model/do/dev_device_tree.go new file mode 100644 index 0000000..cac0379 --- /dev/null +++ b/internal/model/do/dev_device_tree.go @@ -0,0 +1,17 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// DevDeviceTree is the golang structure of table dev_device_tree for DAO operations like Where/Data. +type DevDeviceTree struct { + g.Meta `orm:"table:dev_device_tree, do:true"` + Id interface{} // + InfoId interface{} // 设备树信息ID + ParentInfoId interface{} // 父ID +} diff --git a/internal/model/do/dev_device_tree_info.go b/internal/model/do/dev_device_tree_info.go new file mode 100644 index 0000000..0e12231 --- /dev/null +++ b/internal/model/do/dev_device_tree_info.go @@ -0,0 +1,41 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// DevDeviceTreeInfo is the golang structure of table dev_device_tree_info for DAO operations like Where/Data. +type DevDeviceTreeInfo struct { + g.Meta `orm:"table:dev_device_tree_info, do:true"` + Id interface{} // + DeptId interface{} // 部门ID + Name interface{} // 名称 + Code interface{} // 编码 + DeviceKey interface{} // 设备标识 + Company interface{} // 所属公司 + Area interface{} // 区域 + Address interface{} // 地址 + Lng interface{} // 经度 + Lat interface{} // 纬度 + Contact interface{} // 联系人 + Phone interface{} // 联系电话 + StartDate *gtime.Time // 服务周期:开始日期 + EndDate *gtime.Time // 服务周期:截止日期 + Image interface{} // 图片 + Duration interface{} // 时间窗口值 + TimeUnit interface{} // 时间单位:1=秒,2=分钟,3=小时,4=天 + Template interface{} // 页面模板,默认:default + Category interface{} // 分类 + Types interface{} // 类型 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 + DeletedBy interface{} // 删除者 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 + DeletedAt *gtime.Time // 删除时间 +} diff --git a/internal/model/do/dev_product.go b/internal/model/do/dev_product.go index 9c86712..27a7a78 100644 --- a/internal/model/do/dev_product.go +++ b/internal/model/do/dev_product.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,21 +13,28 @@ import ( type DevProduct struct { g.Meta `orm:"table:dev_product, do:true"` Id interface{} // + DeptId interface{} // 部门ID Key interface{} // 产品标识 Name interface{} // 产品名称 CategoryId interface{} // 所属品类 MessageProtocol interface{} // 消息协议 TransportProtocol interface{} // 传输协议: MQTT,COAP,UDP ProtocolId interface{} // 协议id - DeviceType interface{} // 设备类型: 网关,设备 + DeviceType interface{} // 设备类型: 网关,设备,子设备 Desc interface{} // 描述 Icon interface{} // 图片地址 Metadata interface{} // 物模型 MetadataTable interface{} // 是否生成物模型表:0=否,1=是 Policy interface{} // 采集策略 Status interface{} // 发布状态:0=未发布,1=已发布 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + AuthType interface{} // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser interface{} // 认证用户 + AuthPasswd interface{} // 认证密码 + AccessToken interface{} // AccessToken + CertificateId interface{} // 证书ID + ScriptInfo interface{} // 脚本信息 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 DeletedBy interface{} // 删除者 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/dev_product_category.go b/internal/model/do/dev_product_category.go index 86035fe..0fff99d 100644 --- a/internal/model/do/dev_product_category.go +++ b/internal/model/do/dev_product_category.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,12 +13,14 @@ import ( type DevProductCategory struct { g.Meta `orm:"table:dev_product_category, do:true"` Id interface{} // + DeptId interface{} // 部门ID ParentId interface{} // 父ID Key interface{} // 分类标识 Name interface{} // 分类名称 + Sort interface{} // 排序 Desc interface{} // 描述 - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 DeletedBy interface{} // 删除者 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/network_server.go b/internal/model/do/network_server.go index f0c9100..adf4953 100644 --- a/internal/model/do/network_server.go +++ b/internal/model/do/network_server.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -11,18 +11,26 @@ import ( // NetworkServer is the golang structure of table network_server for DAO operations like Where/Data. type NetworkServer struct { - g.Meta `orm:"table:network_server, do:true"` - Id interface{} // - Name interface{} // - Types interface{} // tcp/udp - Addr interface{} // - Register interface{} // 注册包 - Heartbeat interface{} // 心跳包 - Protocol interface{} // 协议 - Devices interface{} // 默认设备 - Status interface{} // - CreatedAt *gtime.Time // - UpdatedAt *gtime.Time // - CreateBy interface{} // - Remark interface{} // 备注 + g.Meta `orm:"table:network_server, do:true"` + Id interface{} // + DeptId interface{} // 部门ID + Name interface{} // + Types interface{} // tcp/udp + Addr interface{} // + Register interface{} // 注册包 + Heartbeat interface{} // 心跳包 + Protocol interface{} // 协议 + Devices interface{} // 默认设备 + Status interface{} // + CreatedAt *gtime.Time // + UpdatedAt *gtime.Time // + CreateBy interface{} // + Remark interface{} // 备注 + IsTls interface{} // 开启TLS:1=是,0=否 + AuthType interface{} // 认证方式(1=Basic,2=AccessToken,3=证书) + AuthUser interface{} // 认证用户 + AuthPasswd interface{} // 认证密码 + AccessToken interface{} // AccessToken + CertificateId interface{} // 证书ID + Stick interface{} // 粘包处理方式 } diff --git a/internal/model/do/network_tunnel.go b/internal/model/do/network_tunnel.go index 23ec00a..224f3bf 100644 --- a/internal/model/do/network_tunnel.go +++ b/internal/model/do/network_tunnel.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type NetworkTunnel struct { g.Meta `orm:"table:network_tunnel, do:true"` Id interface{} // + DeptId interface{} // 部门ID ServerId interface{} // 服务ID Name interface{} // Types interface{} // diff --git a/internal/model/do/notice_config.go b/internal/model/do/notice_config.go index 0d51324..931d6f1 100644 --- a/internal/model/do/notice_config.go +++ b/internal/model/do/notice_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type NoticeConfig struct { g.Meta `orm:"table:notice_config, do:true"` Id interface{} // + DeptId interface{} // 部门ID Title interface{} // SendGateway interface{} // Types interface{} // diff --git a/internal/model/do/notice_info.go b/internal/model/do/notice_info.go index 01676e7..2394892 100644 --- a/internal/model/do/notice_info.go +++ b/internal/model/do/notice_info.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/notice_log.go b/internal/model/do/notice_log.go index 8a80432..d3f3de1 100644 --- a/internal/model/do/notice_log.go +++ b/internal/model/do/notice_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type NoticeLog struct { g.Meta `orm:"table:notice_log, do:true"` Id interface{} // + DeptId interface{} // 部门ID SendGateway interface{} // 通知渠道 TemplateId interface{} // 通知模板ID Addressee interface{} // 收信人列表 diff --git a/internal/model/do/notice_template.go b/internal/model/do/notice_template.go index 57787d1..ededb9b 100644 --- a/internal/model/do/notice_template.go +++ b/internal/model/do/notice_template.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type NoticeTemplate struct { g.Meta `orm:"table:notice_template, do:true"` Id interface{} // + DeptId interface{} // 部门ID ConfigId interface{} // SendGateway interface{} // Code interface{} // diff --git a/internal/model/do/sys_api.go b/internal/model/do/sys_api.go index b700752..50adfaa 100644 --- a/internal/model/do/sys_api.go +++ b/internal/model/do/sys_api.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -16,13 +16,14 @@ type SysApi struct { ParentId interface{} // Name interface{} // 名称 Types interface{} // 1 分类 2接口 + ApiTypes interface{} // 数据字典维护 Method interface{} // 请求方式(数据字典维护) Address interface{} // 接口地址 Remark interface{} // 备注 Status interface{} // 状态 0 停用 1启用 Sort interface{} // 排序 IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建时间 UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改时间 diff --git a/internal/model/do/sys_authorize.go b/internal/model/do/sys_authorize.go index 6cd05aa..125f388 100644 --- a/internal/model/do/sys_authorize.go +++ b/internal/model/do/sys_authorize.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_blacklist.go b/internal/model/do/sys_blacklist.go new file mode 100644 index 0000000..727ebf6 --- /dev/null +++ b/internal/model/do/sys_blacklist.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// SysBlacklist is the golang structure of table sys_blacklist for DAO operations like Where/Data. +type SysBlacklist struct { + g.Meta `orm:"table:sys_blacklist, do:true"` + Id interface{} // 黑名单ID + DeptId interface{} // 部门ID + Ip interface{} // IP地址 + Remark interface{} // 备注 + Status interface{} // 状态 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/internal/model/do/sys_certificate.go b/internal/model/do/sys_certificate.go new file mode 100644 index 0000000..0c86801 --- /dev/null +++ b/internal/model/do/sys_certificate.go @@ -0,0 +1,31 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// SysCertificate is the golang structure of table sys_certificate for DAO operations like Where/Data. +type SysCertificate struct { + g.Meta `orm:"table:sys_certificate, do:true"` + Id interface{} // + DeptId interface{} // 部门ID + Name interface{} // 名称 + Standard interface{} // 证书标准 + FileContent interface{} // 证书文件内容 + PublicKeyContent interface{} // 证书公钥内容 + PrivateKeyContent interface{} // 证书私钥内容 + Description interface{} // 说明 + Status interface{} // 状态 0未启用 1启用 + IsDeleted interface{} // 是否删除 0未删除 1已删除 + CreatedBy interface{} // 创建者 + CreatedAt *gtime.Time // 创建日期 + UpdatedBy interface{} // 修改人 + UpdatedAt *gtime.Time // 更新时间 + DeletedBy interface{} // 删除人 + DeletedAt *gtime.Time // 删除时间 +} diff --git a/internal/model/do/sys_config.go b/internal/model/do/sys_config.go index daedb29..a3f4079 100644 --- a/internal/model/do/sys_config.go +++ b/internal/model/do/sys_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,17 +13,17 @@ import ( type SysConfig struct { g.Meta `orm:"table:sys_config, do:true"` ConfigId interface{} // 参数主键 + ModuleClassify interface{} // 所属字典类型数据code ConfigName interface{} // 参数名称 ConfigKey interface{} // 参数键名 ConfigValue interface{} // 参数键值 ConfigType interface{} // 系统内置(1是 2否) - ModuleClassify interface{} // 字典分类编码 Remark interface{} // 备注 Status interface{} // 状态 0 停用 1启用 IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建时间 - UpdateBy interface{} // 更新者 + UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改时间 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 diff --git a/internal/model/do/sys_dept.go b/internal/model/do/sys_dept.go index 82d53b0..28d382e 100644 --- a/internal/model/do/sys_dept.go +++ b/internal/model/do/sys_dept.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_dict_data.go b/internal/model/do/sys_dict_data.go index 8089d84..dbbfe72 100644 --- a/internal/model/do/sys_dict_data.go +++ b/internal/model/do/sys_dict_data.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -23,9 +23,9 @@ type SysDictData struct { Remark interface{} // 备注 Status interface{} // 状态(0正常 1停用) IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建时间 - UpdateBy interface{} // 更新者 + UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改时间 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 diff --git a/internal/model/do/sys_dict_type.go b/internal/model/do/sys_dict_type.go index ed6d4b9..5620ab8 100644 --- a/internal/model/do/sys_dict_type.go +++ b/internal/model/do/sys_dict_type.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -20,9 +20,9 @@ type SysDictType struct { Remark interface{} // 备注 Status interface{} // 状态(0正常 1停用) IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建日期 - UpdateBy interface{} // 更新者 + UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改日期 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 diff --git a/internal/model/do/sys_job.go b/internal/model/do/sys_job.go index d0c229b..81792ce 100644 --- a/internal/model/do/sys_job.go +++ b/internal/model/do/sys_job.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -21,8 +21,8 @@ type SysJob struct { MisfirePolicy interface{} // 计划执行策略(1多次执行 2执行一次) Concurrent interface{} // 是否并发执行(0允许 1禁止) Status interface{} // 状态(0正常 1暂停) - CreateBy interface{} // 创建者 - UpdateBy interface{} // 更新者 + CreatedBy interface{} // 创建者 + UpdatedBy interface{} // 更新者 Remark interface{} // 备注信息 CreatedAt *gtime.Time // 创建时间 UpdatedAt *gtime.Time // 更新时间 diff --git a/internal/model/do/sys_login_log.go b/internal/model/do/sys_login_log.go index 8fd6538..de3a420 100644 --- a/internal/model/do/sys_login_log.go +++ b/internal/model/do/sys_login_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -18,7 +18,7 @@ type SysLoginLog struct { LoginLocation interface{} // 登录地点 Browser interface{} // 浏览器类型 Os interface{} // 操作系统 - Status interface{} // 登录状态(0成功 1失败) + Status interface{} // 登录状态(0失败 1成功) Msg interface{} // 提示消息 LoginTime *gtime.Time // 登录时间 Module interface{} // 登录模块 diff --git a/internal/model/do/sys_menu.go b/internal/model/do/sys_menu.go index 9166259..b5b54e2 100644 --- a/internal/model/do/sys_menu.go +++ b/internal/model/do/sys_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -15,7 +15,7 @@ type SysMenu struct { Id interface{} // ParentId interface{} // 父ID Name interface{} // 规则名称 - Title interface{} // 规则名称 + Title interface{} // 菜单名称 Icon interface{} // 图标 Condition interface{} // 条件 Remark interface{} // 备注 diff --git a/internal/model/do/sys_menu_api.go b/internal/model/do/sys_menu_api.go index 8f4ed33..62a9bf1 100644 --- a/internal/model/do/sys_menu_api.go +++ b/internal/model/do/sys_menu_api.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_menu_button.go b/internal/model/do/sys_menu_button.go index 9ebeeff..57a27e1 100644 --- a/internal/model/do/sys_menu_button.go +++ b/internal/model/do/sys_menu_button.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_menu_column.go b/internal/model/do/sys_menu_column.go index 7852da9..12ed049 100644 --- a/internal/model/do/sys_menu_column.go +++ b/internal/model/do/sys_menu_column.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/city_data.go b/internal/model/do/sys_message.go similarity index 51% rename from internal/model/do/city_data.go rename to internal/model/do/sys_message.go index d858ae6..372ddee 100644 --- a/internal/model/do/city_data.go +++ b/internal/model/do/sys_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -9,20 +9,17 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// CityData is the golang structure of table city_data for DAO operations like Where/Data. -type CityData struct { - g.Meta `orm:"table:city_data, do:true"` +// SysMessage is the golang structure of table sys_message for DAO operations like Where/Data. +type SysMessage struct { + g.Meta `orm:"table:sys_message, do:true"` Id interface{} // - Name interface{} // 名字 - Code interface{} // 编码 - ParentId interface{} // 父ID - Sort interface{} // 排序 - Status interface{} // 状态;0:禁用;1:正常 + Title interface{} // 标题 + Types interface{} // 字典表 + Scope interface{} // 消息范围 + Content interface{} // 内容 IsDeleted interface{} // 是否删除 0未删除 1已删除 CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建日期 - UpdatedBy interface{} // 更新者 - UpdatedAt *gtime.Time // 修改日期 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 } diff --git a/internal/model/do/sys_messagereceive.go b/internal/model/do/sys_messagereceive.go new file mode 100644 index 0000000..b7b0902 --- /dev/null +++ b/internal/model/do/sys_messagereceive.go @@ -0,0 +1,23 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// SysMessagereceive is the golang structure of table sys_messagereceive for DAO operations like Where/Data. +type SysMessagereceive struct { + g.Meta `orm:"table:sys_messagereceive, do:true"` + Id interface{} // + UserId interface{} // 用户ID + MessageId interface{} // 消息ID + IsRead interface{} // 是否已读 0 未读 1已读 + IsPush interface{} // 是否已经推送0 否 1是 + IsDeleted interface{} // 是否删除 0未删除 1已删除 + ReadTime *gtime.Time // 阅读时间 + DeletedAt *gtime.Time // 删除时间 +} diff --git a/internal/model/do/sys_notifications.go b/internal/model/do/sys_notifications.go index 2f92622..308d9e2 100644 --- a/internal/model/do/sys_notifications.go +++ b/internal/model/do/sys_notifications.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_oper_log.go b/internal/model/do/sys_oper_log.go index d8180e6..c816059 100644 --- a/internal/model/do/sys_oper_log.go +++ b/internal/model/do/sys_oper_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -25,7 +25,7 @@ type SysOperLog struct { OperLocation interface{} // 操作地点 OperParam interface{} // 请求参数 JsonResult interface{} // 返回参数 - Status interface{} // 操作状态(0正常 1异常) + Status interface{} // 操作状态(0异常 1正常) ErrorMsg interface{} // 错误消息 OperTime *gtime.Time // 操作时间 } diff --git a/internal/model/do/sys_organization.go b/internal/model/do/sys_organization.go index 1e34785..9723712 100644 --- a/internal/model/do/sys_organization.go +++ b/internal/model/do/sys_organization.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type SysOrganization struct { g.Meta `orm:"table:sys_organization, do:true"` Id interface{} // 组织ID + DeptId interface{} // 部门ID ParentId interface{} // 父组织id Ancestors interface{} // 祖级列表 Name interface{} // 组织名称 diff --git a/internal/model/do/sys_plugins.go b/internal/model/do/sys_plugins.go index 0bcbd00..8fc5009 100644 --- a/internal/model/do/sys_plugins.go +++ b/internal/model/do/sys_plugins.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -11,14 +11,30 @@ import ( // SysPlugins is the golang structure of table sys_plugins for DAO operations like Where/Data. type SysPlugins struct { - g.Meta `orm:"table:sys_plugins, do:true"` - Id interface{} // ID - Name interface{} // 名称 - Title interface{} // 标题 - Intro interface{} // 介绍 - Version interface{} // 版本 - Status interface{} // 状态 - Types interface{} // 插件类型 - Author interface{} // - StartTime *gtime.Time // + g.Meta `orm:"table:sys_plugins, do:true"` + Id interface{} // ID + DeptId interface{} // 部门ID + Types interface{} // 插件与SagooIOT的通信方式 + HandleType interface{} // 功能类型 + Name interface{} // 名称 + Title interface{} // 标题 + Description interface{} // 介绍 + Version interface{} // 版本 + Author interface{} // 作者 + Icon interface{} // 插件图标 + Link interface{} // 插件的网址。指向插件的 github 链接。值应为一个可访问的网址 + Command interface{} // 插件的运行指令 + Args interface{} // 插件的指令参数 + Status interface{} // 状态 0未启用 1启用 + FrontendUi interface{} // 是否有插件页面 + FrontendUrl interface{} // 插件页面地址 + FrontendConfiguration interface{} // 是否显示配置页面 + StartTime *gtime.Time // 启动时间 + IsDeleted interface{} // 是否删除 0未删除 1已删除 + CreatedBy interface{} // 创建者 + CreatedAt *gtime.Time // 创建日期 + UpdatedBy interface{} // 修改人 + UpdatedAt *gtime.Time // 更新时间 + DeletedBy interface{} // 删除人 + DeletedAt *gtime.Time // 删除时间 } diff --git a/internal/model/do/sys_plugins_config.go b/internal/model/do/sys_plugins_config.go index 755e63e..1c0fd23 100644 --- a/internal/model/do/sys_plugins_config.go +++ b/internal/model/do/sys_plugins_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_post.go b/internal/model/do/sys_post.go index 126ddde..3664d6a 100644 --- a/internal/model/do/sys_post.go +++ b/internal/model/do/sys_post.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type SysPost struct { g.Meta `orm:"table:sys_post, do:true"` PostId interface{} // 岗位ID + DeptId interface{} // 部门ID ParentId interface{} // 父ID PostCode interface{} // 岗位编码 PostName interface{} // 岗位名称 diff --git a/internal/model/do/sys_role.go b/internal/model/do/sys_role.go index 98581e7..d5fc69a 100644 --- a/internal/model/do/sys_role.go +++ b/internal/model/do/sys_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -13,6 +13,7 @@ import ( type SysRole struct { g.Meta `orm:"table:sys_role, do:true"` Id interface{} // + DeptId interface{} // 部门ID ParentId interface{} // 父ID ListOrder interface{} // 排序 Name interface{} // 角色名称 @@ -20,9 +21,9 @@ type SysRole struct { Remark interface{} // 备注 Status interface{} // 状态;0:禁用;1:正常 IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建日期 - UpdateBy interface{} // 更新者 + UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改日期 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 diff --git a/internal/model/do/sys_role_dept.go b/internal/model/do/sys_role_dept.go index df1a1ad..c3795b6 100644 --- a/internal/model/do/sys_role_dept.go +++ b/internal/model/do/sys_role_dept.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_user.go b/internal/model/do/sys_user.go index 835e444..b799421 100644 --- a/internal/model/do/sys_user.go +++ b/internal/model/do/sys_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do @@ -17,7 +17,7 @@ type SysUser struct { UserTypes interface{} // 系统 system 企业 company Mobile interface{} // 中国手机不带国家代码,国际手机号格式为:国家代码-手机号 UserNickname interface{} // 用户昵称 - Birthday interface{} // 生日 + Birthday *gtime.Time // 生日 UserPassword interface{} // 登录密码;cmf_password加密 UserSalt interface{} // 加密盐 UserEmail interface{} // 用户登录邮箱 @@ -32,9 +32,9 @@ type SysUser struct { LastLoginTime *gtime.Time // 最后登录时间 Status interface{} // 用户状态;0:禁用,1:正常,2:未验证 IsDeleted interface{} // 是否删除 0未删除 1已删除 - CreateBy interface{} // 创建者 + CreatedBy interface{} // 创建者 CreatedAt *gtime.Time // 创建日期 - UpdateBy interface{} // 更新者 + UpdatedBy interface{} // 更新者 UpdatedAt *gtime.Time // 修改日期 DeletedBy interface{} // 删除人 DeletedAt *gtime.Time // 删除时间 diff --git a/internal/model/do/sys_user_online.go b/internal/model/do/sys_user_online.go index 5e10120..848274d 100644 --- a/internal/model/do/sys_user_online.go +++ b/internal/model/do/sys_user_online.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_user_password_history.go b/internal/model/do/sys_user_password_history.go new file mode 100644 index 0000000..8ba8aae --- /dev/null +++ b/internal/model/do/sys_user_password_history.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// SysUserPasswordHistory is the golang structure of table sys_user_password_history for DAO operations like Where/Data. +type SysUserPasswordHistory struct { + g.Meta `orm:"table:sys_user_password_history, do:true"` + Id interface{} // + UserId interface{} // 用户ID + BeforePassword interface{} // 变更之前密码 + AfterPassword interface{} // 变更之后密码 + ChangeTime *gtime.Time // 变更时间 + CreatedAt *gtime.Time // + CreatedBy interface{} // +} diff --git a/internal/model/do/sys_user_post.go b/internal/model/do/sys_user_post.go index a06c02d..5702e16 100644 --- a/internal/model/do/sys_user_post.go +++ b/internal/model/do/sys_user_post.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/do/sys_user_role.go b/internal/model/do/sys_user_role.go index de3ce76..1827c78 100644 --- a/internal/model/do/sys_user_role.go +++ b/internal/model/do/sys_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package do diff --git a/internal/model/entity/alarm_level.go b/internal/model/entity/alarm_level.go index 0ecdadb..1f2b404 100644 --- a/internal/model/entity/alarm_level.go +++ b/internal/model/entity/alarm_level.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/alarm_log.go b/internal/model/entity/alarm_log.go index 985ffbc..e08e74a 100644 --- a/internal/model/entity/alarm_log.go +++ b/internal/model/entity/alarm_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,16 +11,18 @@ import ( // AlarmLog is the golang structure for table alarm_log. type AlarmLog struct { Id int64 `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Type uint `json:"type" description:"告警类型:1=规则告警,2=设备自主告警"` RuleId uint64 `json:"ruleId" description:"规则id"` RuleName string `json:"ruleName" description:"规则名称"` Level uint `json:"level" description:"告警级别"` Data string `json:"data" description:"触发告警的数据"` + Expression string `json:"expression" description:"触发告警的表达式"` ProductKey string `json:"productKey" description:"产品标识"` DeviceKey string `json:"deviceKey" description:"设备标识"` Status int `json:"status" description:"告警状态:0=未处理,1=已处理"` CreatedAt *gtime.Time `json:"createdAt" description:"告警时间"` - UpdateBy uint `json:"updateBy" description:"告警处理人员"` + UpdatedBy uint `json:"updatedBy" description:"告警处理人员"` UpdatedAt *gtime.Time `json:"updatedAt" description:"处理时间"` Content string `json:"content" description:"处理意见"` } diff --git a/internal/model/entity/alarm_rule.go b/internal/model/entity/alarm_rule.go index 24bcb52..17d6c78 100644 --- a/internal/model/entity/alarm_rule.go +++ b/internal/model/entity/alarm_rule.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,16 +11,19 @@ import ( // AlarmRule is the golang structure for table alarm_rule. type AlarmRule struct { Id uint64 `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Name string `json:"name" description:"告警规则名称"` Level uint `json:"level" description:"告警级别,默认:4(一般)"` ProductKey string `json:"productKey" description:"产品标识"` DeviceKey string `json:"deviceKey" description:"设备标识"` - TriggerType int `json:"triggerType" description:"触发类型:1=上线,2=离线,3=属性上报"` + TriggerMode int `json:"triggerMode" description:"触发方式:1=设备触发,2=定时触发"` + TriggerType int `json:"triggerType" description:"触发类型:1=上线,2=离线,3=属性上报, 4=事件上报"` + EventKey string `json:"eventKey" description:"事件标识"` TriggerCondition string `json:"triggerCondition" description:"触发条件"` Action string `json:"action" description:"执行动作"` Status int `json:"status" description:"状态:0=未启用,1=已启用"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` DeletedBy uint `json:"deletedBy" description:"删除者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/base_db_link.go b/internal/model/entity/base_db_link.go deleted file mode 100644 index 986be49..0000000 --- a/internal/model/entity/base_db_link.go +++ /dev/null @@ -1,29 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// BaseDbLink is the golang structure for table base_db_link. -type BaseDbLink struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名称"` - Types string `json:"types" description:"驱动类型 mysql或oracle"` - Host string `json:"host" description:"主机地址"` - Port int `json:"port" description:"端口号"` - UserName string `json:"userName" description:"用户名称"` - Password string `json:"password" description:"密码"` - Description string `json:"description" description:"描述"` - Status int `json:"status" description:"状态 0 停用 1启用"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreatedBy uint `json:"createdBy" description:"创建人"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedBy int `json:"updatedBy" description:"修改人"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` - DeletedBy int `json:"deletedBy" description:"删除人"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/casbin_rule.go b/internal/model/entity/casbin_rule.go new file mode 100644 index 0000000..7b215ac --- /dev/null +++ b/internal/model/entity/casbin_rule.go @@ -0,0 +1,16 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// CasbinRule is the golang structure for table casbin_rule. +type CasbinRule struct { + Ptype string `json:"ptype" description:""` + V0 string `json:"v0" description:""` + V1 string `json:"v1" description:""` + V2 string `json:"v2" description:""` + V3 string `json:"v3" description:""` + V4 string `json:"v4" description:""` + V5 string `json:"v5" description:""` +} diff --git a/internal/model/entity/data_node.go b/internal/model/entity/data_node.go deleted file mode 100644 index f18f5ad..0000000 --- a/internal/model/entity/data_node.go +++ /dev/null @@ -1,27 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// DataNode is the golang structure for table data_node. -type DataNode struct { - NodeId uint64 `json:"nodeId" description:""` - SourceId uint64 `json:"sourceId" description:"数据源ID"` - Name string `json:"name" description:"数据节点名称"` - Key string `json:"key" description:"数据节点标识"` - DataType string `json:"dataType" description:"数据类型"` - Value string `json:"value" description:"取值项"` - IsPk int `json:"isPk" description:"是否主键:0=否,1=是"` - Rule string `json:"rule" description:"规则配置json"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` - DeletedBy uint `json:"deletedBy" description:"删除者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/data_source.go b/internal/model/entity/data_source.go deleted file mode 100644 index 1fb193a..0000000 --- a/internal/model/entity/data_source.go +++ /dev/null @@ -1,29 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// DataSource is the golang structure for table data_source. -type DataSource struct { - SourceId uint64 `json:"sourceId" description:""` - Name string `json:"name" description:"数据源名称"` - Key string `json:"key" description:"数据源标识"` - Desc string `json:"desc" description:"描述"` - From int `json:"from" description:"数据来源:1=api导入,2=数据库,3=文件,4=设备"` - Config string `json:"config" description:"数据源配置json:api配置、数据库配置、文件配置"` - Rule string `json:"rule" description:"规则配置json"` - LockKey int `json:"lockKey" description:"锁定key标识:0=未锁定,1=锁定,不允许修改"` - Status int `json:"status" description:"状态:0=未发布,1=已发布"` - DataTable string `json:"dataTable" description:"数据表名称"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` - DeletedBy uint `json:"deletedBy" description:"删除者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/data_template.go b/internal/model/entity/data_template.go deleted file mode 100644 index 3151baf..0000000 --- a/internal/model/entity/data_template.go +++ /dev/null @@ -1,31 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplate is the golang structure for table data_template. -type DataTemplate struct { - Id uint64 `json:"id" description:"ID"` - Name string `json:"name" description:"名称"` - Key string `json:"key" description:"标识"` - Desc string `json:"desc" description:"描述"` - Status int `json:"status" description:"状态:0=未发布,1=已发布"` - CronExpression string `json:"cronExpression" description:"cron执行表达式"` - SortNodeKey string `json:"sortNodeKey" description:"排序节点标识"` - SortDesc int `json:"sortDesc" description:"排序方式:1=倒序,2=正序"` - DataTable string `json:"dataTable" description:"数据表名称"` - LockKey int `json:"lockKey" description:"锁定key标识:0=未锁定,1=锁定,不允许修改"` - MainSourceId uint64 `json:"mainSourceId" description:"主数据源"` - SourceNodeKey string `json:"sourceNodeKey" description:"数据源关联节点"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` - DeletedBy uint `json:"deletedBy" description:"删除者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/data_template_busi.go b/internal/model/entity/data_template_busi.go deleted file mode 100644 index 4324114..0000000 --- a/internal/model/entity/data_template_busi.go +++ /dev/null @@ -1,21 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplateBusi is the golang structure for table data_template_busi. -type DataTemplateBusi struct { - Id int `json:"id" description:""` - DataTemplateId int `json:"dataTemplateId" description:"数据建模ID"` - BusiTypes int `json:"busiTypes" description:"业务单元"` - IsDeleted int `json:"isDeleted" description:"0未删除 1已删除"` - CreatedBy uint `json:"createdBy" description:"创建人"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - DeletedBy int `json:"deletedBy" description:"删除人"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/data_template_node.go b/internal/model/entity/data_template_node.go deleted file mode 100644 index c2a70e3..0000000 --- a/internal/model/entity/data_template_node.go +++ /dev/null @@ -1,31 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// DataTemplateNode is the golang structure for table data_template_node. -type DataTemplateNode struct { - Id uint64 `json:"id" description:"ID"` - Tid uint64 `json:"tid" description:"模型ID"` - From int `json:"from" description:"字段生成方式:1=自动生成,2=数据源"` - SourceId uint64 `json:"sourceId" description:"数据源ID"` - NodeId uint64 `json:"nodeId" description:"数据源ID"` - Name string `json:"name" description:"节点名称"` - Key string `json:"key" description:"节点标识"` - DataType string `json:"dataType" description:"数据类型"` - Default string `json:"default" description:"默认值"` - Method string `json:"method" description:"数值类型,取值方式"` - IsPk int `json:"isPk" description:"是否主键:0=否,1=是"` - Desc string `json:"desc" description:"描述"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` - DeletedBy uint `json:"deletedBy" description:"删除者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` -} diff --git a/internal/model/entity/dev_device.go b/internal/model/entity/dev_device.go index 3f655b6..af4396d 100644 --- a/internal/model/entity/dev_device.go +++ b/internal/model/entity/dev_device.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,20 +11,27 @@ import ( // DevDevice is the golang structure for table dev_device. type DevDevice struct { Id uint `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Key string `json:"key" description:"设备标识"` Name string `json:"name" description:"设备名称"` - ProductId uint `json:"productId" description:"所属产品"` + ProductKey string `json:"productKey" description:"所属产品KEY"` Desc string `json:"desc" description:"描述"` MetadataTable int `json:"metadataTable" description:"是否生成物模型子表:0=否,1=是"` Status int `json:"status" description:"状态:0=未启用,1=离线,2=在线"` + OnlineTimeout int `json:"onlineTimeout" description:"设备在线超时设置,单位:秒"` RegistryTime *gtime.Time `json:"registryTime" description:"激活时间"` LastOnlineTime *gtime.Time `json:"lastOnlineTime" description:"最后上线时间"` - Certificate string `json:"certificate" description:"设备证书"` - SecureKey string `json:"secureKey" description:"设备密钥"` Version string `json:"version" description:"固件版本号"` TunnelId int `json:"tunnelId" description:"tunnelId"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + Lng string `json:"lng" description:"经度"` + Lat string `json:"lat" description:"纬度"` + AuthType int `json:"authType" description:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" description:"认证用户"` + AuthPasswd string `json:"authPasswd" description:"认证密码"` + AccessToken string `json:"accessToken" description:"AccessToken"` + CertificateId int `json:"certificateId" description:"证书ID"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` DeletedBy uint `json:"deletedBy" description:"删除者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/dev_device_gateway.go b/internal/model/entity/dev_device_gateway.go new file mode 100644 index 0000000..aba736c --- /dev/null +++ b/internal/model/entity/dev_device_gateway.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// DevDeviceGateway is the golang structure for table dev_device_gateway. +type DevDeviceGateway struct { + Id uint `json:"id" description:""` + GatewayKey string `json:"gatewayKey" description:"网关标识"` + SubKey string `json:"subKey" description:"子设备标识"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` + DeletedBy uint `json:"deletedBy" description:"删除者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/entity/dev_device_tag.go b/internal/model/entity/dev_device_tag.go index 0cb8427..ab59800 100644 --- a/internal/model/entity/dev_device_tag.go +++ b/internal/model/entity/dev_device_tag.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,13 +11,14 @@ import ( // DevDeviceTag is the golang structure for table dev_device_tag. type DevDeviceTag struct { Id uint `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` DeviceId uint `json:"deviceId" description:"设备ID"` DeviceKey string `json:"deviceKey" description:"设备标识"` Key string `json:"key" description:"标签标识"` Name string `json:"name" description:"标签名称"` Value string `json:"value" description:"标签值"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` DeletedBy uint `json:"deletedBy" description:"删除者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/dev_device_tree.go b/internal/model/entity/dev_device_tree.go new file mode 100644 index 0000000..216861d --- /dev/null +++ b/internal/model/entity/dev_device_tree.go @@ -0,0 +1,12 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// DevDeviceTree is the golang structure for table dev_device_tree. +type DevDeviceTree struct { + Id int `json:"id" description:""` + InfoId int `json:"infoId" description:"设备树信息ID"` + ParentInfoId int `json:"parentInfoId" description:"父ID"` +} diff --git a/internal/model/entity/dev_device_tree_info.go b/internal/model/entity/dev_device_tree_info.go new file mode 100644 index 0000000..e38dbd4 --- /dev/null +++ b/internal/model/entity/dev_device_tree_info.go @@ -0,0 +1,39 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// DevDeviceTreeInfo is the golang structure for table dev_device_tree_info. +type DevDeviceTreeInfo struct { + Id int `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` + Name string `json:"name" description:"名称"` + Code string `json:"code" description:"编码"` + DeviceKey string `json:"deviceKey" description:"设备标识"` + Company string `json:"company" description:"所属公司"` + Area string `json:"area" description:"区域"` + Address string `json:"address" description:"地址"` + Lng string `json:"lng" description:"经度"` + Lat string `json:"lat" description:"纬度"` + Contact string `json:"contact" description:"联系人"` + Phone string `json:"phone" description:"联系电话"` + StartDate *gtime.Time `json:"startDate" description:"服务周期:开始日期"` + EndDate *gtime.Time `json:"endDate" description:"服务周期:截止日期"` + Image string `json:"image" description:"图片"` + Duration int `json:"duration" description:"时间窗口值"` + TimeUnit int `json:"timeUnit" description:"时间单位:1=秒,2=分钟,3=小时,4=天"` + Template string `json:"template" description:"页面模板,默认:default"` + Category string `json:"category" description:"分类"` + Types string `json:"types" description:"类型"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` + DeletedBy uint `json:"deletedBy" description:"删除者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/entity/dev_product.go b/internal/model/entity/dev_product.go index b7d6b22..19235c2 100644 --- a/internal/model/entity/dev_product.go +++ b/internal/model/entity/dev_product.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,21 +11,28 @@ import ( // DevProduct is the golang structure for table dev_product. type DevProduct struct { Id uint `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Key string `json:"key" description:"产品标识"` Name string `json:"name" description:"产品名称"` CategoryId uint `json:"categoryId" description:"所属品类"` MessageProtocol string `json:"messageProtocol" description:"消息协议"` TransportProtocol string `json:"transportProtocol" description:"传输协议: MQTT,COAP,UDP"` ProtocolId uint `json:"protocolId" description:"协议id"` - DeviceType string `json:"deviceType" description:"设备类型: 网关,设备"` + DeviceType string `json:"deviceType" description:"设备类型: 网关,设备,子设备"` Desc string `json:"desc" description:"描述"` Icon string `json:"icon" description:"图片地址"` Metadata string `json:"metadata" description:"物模型"` MetadataTable int `json:"metadataTable" description:"是否生成物模型表:0=否,1=是"` Policy string `json:"policy" description:"采集策略"` Status int `json:"status" description:"发布状态:0=未发布,1=已发布"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + AuthType int `json:"authType" description:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" description:"认证用户"` + AuthPasswd string `json:"authPasswd" description:"认证密码"` + AccessToken string `json:"accessToken" description:"AccessToken"` + CertificateId int `json:"certificateId" description:"证书ID"` + ScriptInfo string `json:"scriptInfo" description:"脚本信息"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` DeletedBy uint `json:"deletedBy" description:"删除者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/dev_product_category.go b/internal/model/entity/dev_product_category.go index 54313fb..f5916be 100644 --- a/internal/model/entity/dev_product_category.go +++ b/internal/model/entity/dev_product_category.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,12 +11,14 @@ import ( // DevProductCategory is the golang structure for table dev_product_category. type DevProductCategory struct { Id uint `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` ParentId uint `json:"parentId" description:"父ID"` Key string `json:"key" description:"分类标识"` Name string `json:"name" description:"分类名称"` + Sort int `json:"sort" description:"排序"` Desc string `json:"desc" description:"描述"` - CreateBy uint `json:"createBy" description:"创建者"` - UpdateBy uint `json:"updateBy" description:"更新者"` + CreatedBy uint `json:"createdBy" description:"创建者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` DeletedBy uint `json:"deletedBy" description:"删除者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/gen_table.go b/internal/model/entity/gen_table.go deleted file mode 100644 index 6d4ae05..0000000 --- a/internal/model/entity/gen_table.go +++ /dev/null @@ -1,29 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// GenTable is the golang structure for table gen_table. -type GenTable struct { - Id int64 `json:"id" description:"编号"` - TableName string `json:"tableName" description:"表名称"` - TableComment string `json:"tableComment" description:"表描述"` - ClassName string `json:"className" description:"实体类名称"` - TplCategory string `json:"tplCategory" description:"使用的模板(crud单表操作 tree树表操作)"` - PackageName string `json:"packageName" description:"生成包路径"` - ModuleName string `json:"moduleName" description:"生成模块名"` - BusinessName string `json:"businessName" description:"生成业务名"` - FunctionName string `json:"functionName" description:"生成功能名"` - FunctionAuthor string `json:"functionAuthor" description:"生成功能作者"` - Options string `json:"options" description:"其它生成选项"` - Remark string `json:"remark" description:"备注"` - CreatedBy int `json:"createdBy" description:"创建人"` - UpdatedBy int `json:"updatedBy" description:"修改人"` - CreatedAt *gtime.Time `json:"createdAt" description:""` - UpdatedAt *gtime.Time `json:"updatedAt" description:""` -} diff --git a/internal/model/entity/gen_table_column.go b/internal/model/entity/gen_table_column.go deleted file mode 100644 index 435371d..0000000 --- a/internal/model/entity/gen_table_column.go +++ /dev/null @@ -1,41 +0,0 @@ -// ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ================================================================================= - -package entity - -import ( - "github.com/gogf/gf/v2/os/gtime" -) - -// GenTableColumn is the golang structure for table gen_table_column. -type GenTableColumn struct { - Id int64 `json:"id" description:"编号"` - TableId int64 `json:"tableId" description:"归属表编号"` - ColumnName string `json:"columnName" description:"列名称"` - ColumnComment string `json:"columnComment" description:"列描述"` - ColumnType string `json:"columnType" description:"列类型"` - GoType string `json:"goType" description:"Go类型"` - GoField string `json:"goField" description:"Go字段名"` - HtmlField string `json:"htmlField" description:"html字段名"` - IsPk string `json:"isPk" description:"是否主键(1是)"` - IsIncrement string `json:"isIncrement" description:"是否自增(1是)"` - IsRequired string `json:"isRequired" description:"是否必填(1是)"` - IsInsert string `json:"isInsert" description:"是否为插入字段(1是)"` - IsEdit string `json:"isEdit" description:"是否编辑字段(1是)"` - IsList string `json:"isList" description:"是否列表字段(1是)"` - IsQuery string `json:"isQuery" description:"是否查询字段(1是)"` - QueryType string `json:"queryType" description:"查询方式(等于、不等于、大于、小于、范围)"` - HtmlType string `json:"htmlType" description:"显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)"` - DictType string `json:"dictType" description:"字典类型"` - Sort int `json:"sort" description:"排序"` - LinkTableName string `json:"linkTableName" description:"关联表名"` - LinkTableClass string `json:"linkTableClass" description:"关联表类名"` - LinkTablePackage string `json:"linkTablePackage" description:"关联表包名"` - LinkLabelId string `json:"linkLabelId" description:"关联表键名"` - LinkLabelName string `json:"linkLabelName" description:"关联表字段值"` - CreateBy int `json:"createBy" description:"创建者"` - UpdateBy int `json:"updateBy" description:"更新者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` -} diff --git a/internal/model/entity/network_server.go b/internal/model/entity/network_server.go index d77434a..4f8a12f 100644 --- a/internal/model/entity/network_server.go +++ b/internal/model/entity/network_server.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -10,17 +10,25 @@ import ( // NetworkServer is the golang structure for table network_server. type NetworkServer struct { - Id int `json:"id" description:""` - Name string `json:"name" description:""` - Types string `json:"types" description:"tcp/udp"` - Addr string `json:"addr" description:""` - Register string `json:"register" description:"注册包"` - Heartbeat string `json:"heartbeat" description:"心跳包"` - Protocol string `json:"protocol" description:"协议"` - Devices string `json:"devices" description:"默认设备"` - Status int `json:"status" description:""` - CreatedAt *gtime.Time `json:"createdAt" description:""` - UpdatedAt *gtime.Time `json:"updatedAt" description:""` - CreateBy int `json:"createBy" description:""` - Remark string `json:"remark" description:"备注"` + Id int `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` + Name string `json:"name" description:""` + Types string `json:"types" description:"tcp/udp"` + Addr string `json:"addr" description:""` + Register string `json:"register" description:"注册包"` + Heartbeat string `json:"heartbeat" description:"心跳包"` + Protocol string `json:"protocol" description:"协议"` + Devices string `json:"devices" description:"默认设备"` + Status int `json:"status" description:""` + CreatedAt *gtime.Time `json:"createdAt" description:""` + UpdatedAt *gtime.Time `json:"updatedAt" description:""` + CreateBy int `json:"createBy" description:""` + Remark string `json:"remark" description:"备注"` + IsTls uint `json:"isTls" description:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" description:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" description:"认证用户"` + AuthPasswd string `json:"authPasswd" description:"认证密码"` + AccessToken string `json:"accessToken" description:"AccessToken"` + CertificateId int `json:"certificateId" description:"证书ID"` + Stick string `json:"stick" description:"粘包处理方式"` } diff --git a/internal/model/entity/network_tunnel.go b/internal/model/entity/network_tunnel.go index e76f96a..570c5ab 100644 --- a/internal/model/entity/network_tunnel.go +++ b/internal/model/entity/network_tunnel.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // NetworkTunnel is the golang structure for table network_tunnel. type NetworkTunnel struct { Id int `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` ServerId int `json:"serverId" description:"服务ID"` Name string `json:"name" description:""` Types string `json:"types" description:""` diff --git a/internal/model/entity/notice_config.go b/internal/model/entity/notice_config.go index b04644b..53a4c93 100644 --- a/internal/model/entity/notice_config.go +++ b/internal/model/entity/notice_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // NoticeConfig is the golang structure for table notice_config. type NoticeConfig struct { Id string `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Title string `json:"title" description:""` SendGateway string `json:"sendGateway" description:""` Types int `json:"types" description:""` diff --git a/internal/model/entity/notice_info.go b/internal/model/entity/notice_info.go index 41931e2..7ca15ef 100644 --- a/internal/model/entity/notice_info.go +++ b/internal/model/entity/notice_info.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/notice_log.go b/internal/model/entity/notice_log.go index 64003f2..bb79458 100644 --- a/internal/model/entity/notice_log.go +++ b/internal/model/entity/notice_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // NoticeLog is the golang structure for table notice_log. type NoticeLog struct { Id uint64 `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` SendGateway string `json:"sendGateway" description:"通知渠道"` TemplateId string `json:"templateId" description:"通知模板ID"` Addressee string `json:"addressee" description:"收信人列表"` diff --git a/internal/model/entity/notice_template.go b/internal/model/entity/notice_template.go index 9be2050..7a12823 100644 --- a/internal/model/entity/notice_template.go +++ b/internal/model/entity/notice_template.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // NoticeTemplate is the golang structure for table notice_template. type NoticeTemplate struct { Id string `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` ConfigId string `json:"configId" description:""` SendGateway string `json:"sendGateway" description:""` Code string `json:"code" description:""` diff --git a/internal/model/entity/sys_api.go b/internal/model/entity/sys_api.go index 1ddb638..808be95 100644 --- a/internal/model/entity/sys_api.go +++ b/internal/model/entity/sys_api.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -14,13 +14,14 @@ type SysApi struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` Status int `json:"status" description:"状态 0 停用 1启用"` Sort int `json:"sort" description:"排序"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` diff --git a/internal/model/entity/sys_authorize.go b/internal/model/entity/sys_authorize.go index a876e14..3db8793 100644 --- a/internal/model/entity/sys_authorize.go +++ b/internal/model/entity/sys_authorize.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_certificate.go b/internal/model/entity/sys_certificate.go new file mode 100644 index 0000000..da92a70 --- /dev/null +++ b/internal/model/entity/sys_certificate.go @@ -0,0 +1,29 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// SysCertificate is the golang structure for table sys_certificate. +type SysCertificate struct { + Id int `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` + Status int `json:"status" description:"状态 0未启用 1启用"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/entity/sys_config.go b/internal/model/entity/sys_config.go index ea141f1..3fc44ce 100644 --- a/internal/model/entity/sys_config.go +++ b/internal/model/entity/sys_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -10,19 +10,19 @@ import ( // SysConfig is the golang structure for table sys_config. type SysConfig struct { - ConfigId uint `json:"configId" description:"参数主键"` - ConfigName string `json:"configName" description:"参数名称"` - ConfigKey string `json:"configKey" description:"参数键名"` - ConfigValue string `json:"configValue" description:"参数键值"` - ConfigType int `json:"configType" description:"系统内置(1是 2否)"` - ModuleClassify string `json:"moduleClassify" description:"字典分类编码"` - Remark string `json:"remark" description:"备注"` - Status int `json:"status" description:"状态 0 停用 1启用"` - IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` - CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdateBy uint `json:"updateBy" description:"更新者"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` - DeletedBy int `json:"deletedBy" description:"删除人"` - DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` + ConfigId uint `json:"configId" description:"参数主键"` + ModuleClassify string `json:"moduleClassify" description:"所属字典类型数据code"` + ConfigName string `json:"configName" description:"参数名称"` + ConfigKey string `json:"configKey" description:"参数键名"` + ConfigValue string `json:"configValue" description:"参数键值"` + ConfigType int `json:"configType" description:"系统内置(1是 2否)"` + Remark string `json:"remark" description:"备注"` + Status int `json:"status" description:"状态 0 停用 1启用"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` } diff --git a/internal/model/entity/sys_dept.go b/internal/model/entity/sys_dept.go index 595ae33..0cf068b 100644 --- a/internal/model/entity/sys_dept.go +++ b/internal/model/entity/sys_dept.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_dict_data.go b/internal/model/entity/sys_dict_data.go index fea82b5..901655f 100644 --- a/internal/model/entity/sys_dict_data.go +++ b/internal/model/entity/sys_dict_data.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -19,11 +19,11 @@ type SysDictData struct { ListClass string `json:"listClass" description:"表格回显样式"` IsDefault int `json:"isDefault" description:"是否默认(1是 0否)"` Remark string `json:"remark" description:"备注"` - Status int `json:"status" description:"状态(1正常 0停用)"` + Status int `json:"status" description:"状态(0正常 1停用)"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` DeletedBy int `json:"deletedBy" description:"删除人"` DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` diff --git a/internal/model/entity/sys_dict_type.go b/internal/model/entity/sys_dict_type.go index f19b4ae..771753b 100644 --- a/internal/model/entity/sys_dict_type.go +++ b/internal/model/entity/sys_dict_type.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -18,9 +18,9 @@ type SysDictType struct { Remark string `json:"remark" description:"备注"` Status uint `json:"status" description:"状态(0正常 1停用)"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` DeletedBy int `json:"deletedBy" description:"删除人"` DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` diff --git a/internal/model/entity/sys_job.go b/internal/model/entity/sys_job.go index 6065a5d..26b8de3 100644 --- a/internal/model/entity/sys_job.go +++ b/internal/model/entity/sys_job.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -19,8 +19,8 @@ type SysJob struct { MisfirePolicy int `json:"misfirePolicy" description:"计划执行策略(1多次执行 2执行一次)"` Concurrent int `json:"concurrent" description:"是否并发执行(0允许 1禁止)"` Status int `json:"status" description:"状态(0正常 1暂停)"` - CreateBy uint64 `json:"createBy" description:"创建者"` - UpdateBy uint64 `json:"updateBy" description:"更新者"` + CreatedBy uint64 `json:"createdBy" description:"创建者"` + UpdatedBy uint64 `json:"updatedBy" description:"更新者"` Remark string `json:"remark" description:"备注信息"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` diff --git a/internal/model/entity/sys_login_log.go b/internal/model/entity/sys_login_log.go index 3a7b961..5876d91 100644 --- a/internal/model/entity/sys_login_log.go +++ b/internal/model/entity/sys_login_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -16,7 +16,7 @@ type SysLoginLog struct { LoginLocation string `json:"loginLocation" description:"登录地点"` Browser string `json:"browser" description:"浏览器类型"` Os string `json:"os" description:"操作系统"` - Status int `json:"status" description:"登录状态(0成功 1失败)"` + Status int `json:"status" description:"登录状态(0失败 1成功)"` Msg string `json:"msg" description:"提示消息"` LoginTime *gtime.Time `json:"loginTime" description:"登录时间"` Module string `json:"module" description:"登录模块"` diff --git a/internal/model/entity/sys_menu.go b/internal/model/entity/sys_menu.go index fe5e769..eeb1d64 100644 --- a/internal/model/entity/sys_menu.go +++ b/internal/model/entity/sys_menu.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -13,7 +13,7 @@ type SysMenu struct { Id uint `json:"id" description:""` ParentId int `json:"parentId" description:"父ID"` Name string `json:"name" description:"规则名称"` - Title string `json:"title" description:"规则名称"` + Title string `json:"title" description:"菜单名称"` Icon string `json:"icon" description:"图标"` Condition string `json:"condition" description:"条件"` Remark string `json:"remark" description:"备注"` diff --git a/internal/model/entity/sys_menu_api.go b/internal/model/entity/sys_menu_api.go index 65f9d57..3abc92c 100644 --- a/internal/model/entity/sys_menu_api.go +++ b/internal/model/entity/sys_menu_api.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_menu_button.go b/internal/model/entity/sys_menu_button.go index 8d22b37..7dae830 100644 --- a/internal/model/entity/sys_menu_button.go +++ b/internal/model/entity/sys_menu_button.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_menu_column.go b/internal/model/entity/sys_menu_column.go index ccba0f1..e45d2d3 100644 --- a/internal/model/entity/sys_menu_column.go +++ b/internal/model/entity/sys_menu_column.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/city_data.go b/internal/model/entity/sys_message.go similarity index 52% rename from internal/model/entity/city_data.go rename to internal/model/entity/sys_message.go index 2e4e014..240eab1 100644 --- a/internal/model/entity/city_data.go +++ b/internal/model/entity/sys_message.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -8,19 +8,16 @@ import ( "github.com/gogf/gf/v2/os/gtime" ) -// CityData is the golang structure for table city_data. -type CityData struct { +// SysMessage is the golang structure for table sys_message. +type SysMessage struct { Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - ParentId int `json:"parentId" description:"父ID"` - Sort int `json:"sort" description:"排序"` - Status uint `json:"status" description:"状态;0:禁用;1:正常"` + Title string `json:"title" description:"标题"` + Types int `json:"types" description:"字典表"` + Scope int `json:"scope" description:"消息范围"` + Content string `json:"content" description:"内容"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdatedBy uint `json:"updatedBy" description:"更新者"` - UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` DeletedBy int `json:"deletedBy" description:"删除人"` DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` } diff --git a/internal/model/entity/sys_messagereceive.go b/internal/model/entity/sys_messagereceive.go new file mode 100644 index 0000000..caeee17 --- /dev/null +++ b/internal/model/entity/sys_messagereceive.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// SysMessagereceive is the golang structure for table sys_messagereceive. +type SysMessagereceive struct { + Id int `json:"id" description:""` + UserId int `json:"userId" description:"用户ID"` + MessageId int `json:"messageId" description:"消息ID"` + IsRead int `json:"isRead" description:"是否已读 0 未读 1已读"` + IsPush int `json:"isPush" description:"是否已经推送0 否 1是"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + ReadTime *gtime.Time `json:"readTime" description:"阅读时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/entity/sys_notifications.go b/internal/model/entity/sys_notifications.go index 2f395e7..b6f5b8d 100644 --- a/internal/model/entity/sys_notifications.go +++ b/internal/model/entity/sys_notifications.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_oper_log.go b/internal/model/entity/sys_oper_log.go index c68385d..f68ecb8 100644 --- a/internal/model/entity/sys_oper_log.go +++ b/internal/model/entity/sys_oper_log.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -23,7 +23,7 @@ type SysOperLog struct { OperLocation string `json:"operLocation" description:"操作地点"` OperParam string `json:"operParam" description:"请求参数"` JsonResult string `json:"jsonResult" description:"返回参数"` - Status int `json:"status" description:"操作状态(0正常 1异常)"` + Status int `json:"status" description:"操作状态(0异常 1正常)"` ErrorMsg string `json:"errorMsg" description:"错误消息"` OperTime *gtime.Time `json:"operTime" description:"操作时间"` } diff --git a/internal/model/entity/sys_organization.go b/internal/model/entity/sys_organization.go index 9d6c491..c3c6dab 100644 --- a/internal/model/entity/sys_organization.go +++ b/internal/model/entity/sys_organization.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // SysOrganization is the golang structure for table sys_organization. type SysOrganization struct { Id int64 `json:"id" description:"组织ID"` + DeptId int `json:"deptId" description:"部门ID"` ParentId int64 `json:"parentId" description:"父组织id"` Ancestors string `json:"ancestors" description:"祖级列表"` Name string `json:"name" description:"组织名称"` diff --git a/internal/model/entity/sys_plugins.go b/internal/model/entity/sys_plugins.go index dc65205..df63cc8 100644 --- a/internal/model/entity/sys_plugins.go +++ b/internal/model/entity/sys_plugins.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -10,14 +10,29 @@ import ( // SysPlugins is the golang structure for table sys_plugins. type SysPlugins struct { - Id int `json:"id" description:"ID"` - Name string `json:"name" description:"名称"` + Id int `json:"id" description:"ID"` + DeptId int `json:"deptId" description:"部门ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` HandleType string `json:"handleType" description:"功能类型"` - Title string `json:"title" description:"标题"` - Intro string `json:"intro" description:"介绍"` - Version string `json:"version" description:"版本"` - Status int `json:"status" description:"状态"` - Types string `json:"types" description:"插件类型"` - Author string `json:"author" description:""` - StartTime *gtime.Time `json:"startTime" description:""` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args string `json:"args" description:"插件的指令参数"` + Status int `json:"status" description:"状态 0未启用 1启用"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` + StartTime *gtime.Time `json:"startTime" description:"启动时间"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` } diff --git a/internal/model/entity/sys_plugins_config.go b/internal/model/entity/sys_plugins_config.go index 3dd0a66..fc79762 100644 --- a/internal/model/entity/sys_plugins_config.go +++ b/internal/model/entity/sys_plugins_config.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_post.go b/internal/model/entity/sys_post.go index bef0920..4591789 100644 --- a/internal/model/entity/sys_post.go +++ b/internal/model/entity/sys_post.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -10,8 +10,9 @@ import ( // SysPost is the golang structure for table sys_post. type SysPost struct { - PostId int64 `json:"postId" description:"岗位ID"` - ParentId int64 `json:"parentId" description:"父ID"` + PostId uint64 `json:"postId" description:"岗位ID"` + DeptId int `json:"deptId" description:"部门ID"` + ParentId int `json:"parentId" description:"父ID"` PostCode string `json:"postCode" description:"岗位编码"` PostName string `json:"postName" description:"岗位名称"` PostSort int `json:"postSort" description:"显示顺序"` diff --git a/internal/model/entity/sys_role.go b/internal/model/entity/sys_role.go index c0a7382..dbc3ea3 100644 --- a/internal/model/entity/sys_role.go +++ b/internal/model/entity/sys_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -11,6 +11,7 @@ import ( // SysRole is the golang structure for table sys_role. type SysRole struct { Id uint `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` ParentId int `json:"parentId" description:"父ID"` ListOrder uint `json:"listOrder" description:"排序"` Name string `json:"name" description:"角色名称"` @@ -18,9 +19,9 @@ type SysRole struct { Remark string `json:"remark" description:"备注"` Status uint `json:"status" description:"状态;0:禁用;1:正常"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` DeletedBy int `json:"deletedBy" description:"删除人"` DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` diff --git a/internal/model/entity/sys_role_dept.go b/internal/model/entity/sys_role_dept.go index c795ca0..2e5483d 100644 --- a/internal/model/entity/sys_role_dept.go +++ b/internal/model/entity/sys_role_dept.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_user.go b/internal/model/entity/sys_user.go index ba42662..026ed78 100644 --- a/internal/model/entity/sys_user.go +++ b/internal/model/entity/sys_user.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -30,7 +30,7 @@ type SysUser struct { LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` diff --git a/internal/model/entity/sys_user_online.go b/internal/model/entity/sys_user_online.go index f04d6c8..66db826 100644 --- a/internal/model/entity/sys_user_online.go +++ b/internal/model/entity/sys_user_online.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity @@ -10,7 +10,7 @@ import ( // SysUserOnline is the golang structure for table sys_user_online. type SysUserOnline struct { - Id uint `json:"id" description:""` + Id int `json:"id" description:""` Uuid string `json:"uuid" description:"用户标识"` Key string `json:"key" description:""` Token string `json:"token" description:"用户token"` diff --git a/internal/model/entity/sys_user_password_history.go b/internal/model/entity/sys_user_password_history.go new file mode 100644 index 0000000..7486773 --- /dev/null +++ b/internal/model/entity/sys_user_password_history.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// SysUserPasswordHistory is the golang structure for table sys_user_password_history. +type SysUserPasswordHistory struct { + Id int `json:"id" description:""` + UserId int `json:"userId" description:"用户ID"` + BeforePassword string `json:"beforePassword" description:"变更之前密码"` + AfterPassword string `json:"afterPassword" description:"变更之后密码"` + ChangeTime *gtime.Time `json:"changeTime" description:"变更时间"` + CreatedAt *gtime.Time `json:"createdAt" description:""` + CreatedBy int `json:"createdBy" description:""` +} diff --git a/internal/model/entity/sys_user_post.go b/internal/model/entity/sys_user_post.go index d0bdf47..121339a 100644 --- a/internal/model/entity/sys_user_post.go +++ b/internal/model/entity/sys_user_post.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/entity/sys_user_role.go b/internal/model/entity/sys_user_role.go index 4e99f93..c86f831 100644 --- a/internal/model/entity/sys_user_role.go +++ b/internal/model/entity/sys_user_role.go @@ -1,5 +1,5 @@ // ================================================================================= -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // ================================================================================= package entity diff --git a/internal/model/env_weather.go b/internal/model/env_weather.go deleted file mode 100644 index 4f252cd..0000000 --- a/internal/model/env_weather.go +++ /dev/null @@ -1,39 +0,0 @@ -package model - -type CityWeatherListOut struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - Windpower string `json:"windpower" description:"风力级别"` - Sunrise string `json:"sunrise" description:"日出"` - Sunset string `json:"sunset" description:"日落"` - SunshineDuration int `json:"sunshineDuration" description:"日照时长"` - Temperature int `json:"Temperature" description:"气温"` - Weather string `json:"weather" description:"天气现象"` - Winddirection string `json:"winddirection" description:"风向描述"` - Reporttime string `json:"reporttime" description:"发布时间"` -} - -type CityWeatherListRes struct { - Id int `json:"id" description:""` - Name string `json:"name" description:"名字"` - Code string `json:"code" description:"编码"` - Windpower string `json:"windpower" description:"风力级别"` - Sunrise string `json:"sunrise" description:"日出"` - Sunset string `json:"sunset" description:"日落"` - SunshineDuration int `json:"sunshineDuration" description:"日照时长"` - Temperature int `json:"Temperature" description:"气温"` - Weather string `json:"weather" description:"天气现象"` - Winddirection string `json:"winddirection" description:"风向描述"` - Reporttime string `json:"reporttime" description:"发布时间"` -} - -type CityWeatherEchartRes struct { - Value string `json:"value" description:"值"` - Time string `json:"time" description:"时间"` -} - -type CityWeatherEchartOut struct { - Value string `json:"value" description:"值"` - Time string `json:"time" description:"时间"` -} diff --git a/internal/model/gen_table.go b/internal/model/gen_table.go new file mode 100644 index 0000000..ba04028 --- /dev/null +++ b/internal/model/gen_table.go @@ -0,0 +1,80 @@ +package model + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +type GenTableRes struct { + Id int64 `json:"id" description:"编号"` + TableName string `json:"tableName" description:"表名称"` + TableComment string `json:"tableComment" description:"表描述"` + ClassName string `json:"className" description:"实体类名称"` + TplCategory string `json:"tplCategory" description:"使用的模板(crud单表操作 tree树表操作)"` + PackageName string `json:"packageName" description:"生成包路径"` + ModuleName string `json:"moduleName" description:"生成模块名"` + BusinessName string `json:"businessName" description:"生成业务名"` + FunctionName string `json:"functionName" description:"生成功能名"` + FunctionAuthor string `json:"functionAuthor" description:"生成功能作者"` + Options string `json:"options" description:"其它生成选项"` + CreatedAt *gtime.Time `json:"createdAt" description:""` + UpdatedAt *gtime.Time `json:"updatedAt" description:""` + Remark string `json:"remark" description:"备注"` + CreatedBy int `json:"createdBy" description:"创建人"` + UpdatedBy int `json:"updatedBy" description:"修改人"` +} +type GenTableAddInput struct { + TableName string `json:"tableName" description:"表名称"` + TableComment string `json:"tableComment" description:"表描述"` + ClassName string `json:"className" description:"实体类名称"` + TplCategory string `json:"tplCategory" description:"使用的模板(crud单表操作 tree树表操作)"` + PackageName string `json:"packageName" description:"生成包路径"` + ModuleName string `json:"moduleName" description:"生成模块名"` + BusinessName string `json:"businessName" description:"生成业务名"` + FunctionName string `json:"functionName" description:"生成功能名"` + FunctionAuthor string `json:"functionAuthor" description:"生成功能作者"` + Options string `json:"options" description:"其它生成选项"` + Remark string `json:"remark" description:"备注"` + CreatedBy int `json:"createdBy" description:"创建人"` + UpdatedBy int `json:"updatedBy" description:"修改人"` +} +type GenTableEditInput struct { + Id int `json:"id" description:"ID"` + GenTableAddInput +} + +// GenTableExtend 实体扩展 +type GenTableExtend struct { + TableId int64 `orm:"table_id,primary" json:"table_id"` // 编号 + TableName string `orm:"table_name" json:"table_name"` // 表名称 + TableComment string `orm:"table_comment" json:"table_comment"` // 表描述 + ClassName string `orm:"class_name" json:"class_name"` // 实体类名称 + TplCategory string `orm:"tpl_category" json:"tpl_category"` // 使用的模板(crud单表操作 tree树表操作) + PackageName string `orm:"package_name" json:"package_name"` // 生成包路径 + ModuleName string `orm:"module_name" json:"module_name"` // 生成模块名 + BusinessName string `orm:"business_name" json:"business_name"` // 生成业务名 + FunctionName string `orm:"function_name" json:"function_name"` // 生成功能名 + FunctionAuthor string `orm:"function_author" json:"function_author"` // 生成功能作者 + Options string `orm:"options" json:"options"` // 其它生成选项 + CreateBy string `orm:"create_by" json:"create_by"` // 创建者 + CreateTime *gtime.Time `orm:"create_time" json:"create_time"` // 创建时间 + UpdateBy string `orm:"update_by" json:"update_by"` // 更新者 + UpdateTime *gtime.Time `orm:"update_time" json:"update_time"` // 更新时间 + Remark string `orm:"remark" json:"remark"` // 备注 + TreeCode string `json:"tree_code"` // 树编码字段 + TreeParentCode string `json:"tree_parent_code"` // 树父编码字段 + TreeName string `json:"tree_name"` // 树名称字段 + Columns []*GenTableColumnRes `json:"columns"` // 表列信息 + PkColumn *GenTableColumnRes `json:"pkColumn"` // 主键列信息 +} + +// 获取列表api +type GetTableListInput struct { + PaginationInput +} + +// 获取数据库的数据表 +type SelectDbTableListInput struct { + TableName string `json:"tableName" description:"数据表名称" ` + TableComment string `json:"tableComment" description:"数据表描述"` + PaginationInput +} diff --git a/internal/model/gen_table_column.go b/internal/model/gen_table_column.go new file mode 100644 index 0000000..0330e59 --- /dev/null +++ b/internal/model/gen_table_column.go @@ -0,0 +1,73 @@ +package model + +import "github.com/gogf/gf/v2/os/gtime" + +type GenTableColumnRes struct { + Id int64 `json:"id" description:"编号"` + TableId int64 `json:"tableId" description:"归属表编号"` + ColumnName string `json:"columnName" description:"列名称"` + ColumnComment string `json:"columnComment" description:"列描述"` + ColumnType string `json:"columnType" description:"列类型"` + GoType string `json:"goType" description:"Go类型"` + GoField string `json:"goField" description:"Go字段名"` + HtmlField string `json:"htmlField" description:"html字段名"` + IsPk string `json:"isPk" description:"是否主键(1是)"` + IsIncrement string `json:"isIncrement" description:"是否自增(1是)"` + IsRequired string `json:"isRequired" description:"是否必填(1是)"` + IsInsert string `json:"isInsert" description:"是否为插入字段(1是)"` + IsEdit string `json:"isEdit" description:"是否编辑字段(1是)"` + IsList string `json:"isList" description:"是否列表字段(1是)"` + IsQuery string `json:"isQuery" description:"是否查询字段(1是)"` + QueryType string `json:"queryType" description:"查询方式(等于、不等于、大于、小于、范围)"` + HtmlType string `json:"htmlType" description:"显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)"` + DictType string `json:"dictType" description:"字典类型"` + Sort int `json:"sort" description:"排序"` + LinkTableName string `json:"linkTableName" description:"关联表名"` + LinkTableClass string `json:"linkTableClass" description:"关联表类名"` + LinkTablePackage string `json:"linkTablePackage" description:"关联表包名"` + LinkLabelId string `json:"linkLabelId" description:"关联表键名"` + LinkLabelName string `json:"linkLabelName" description:"关联表字段值"` + CreateBy int `json:"createBy" description:"创建者"` + UpdateBy int `json:"updateBy" description:"更新者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} +type GenTableColumnAddInput struct { + TableId int64 `json:"tableId" description:"归属表编号"` + ColumnName string `json:"columnName" description:"列名称"` + ColumnComment string `json:"columnComment" description:"列描述"` + ColumnType string `json:"columnType" description:"列类型"` + GoType string `json:"goType" description:"Go类型"` + GoField string `json:"goField" description:"Go字段名"` + HtmlField string `json:"htmlField" description:"html字段名"` + IsPk string `json:"isPk" description:"是否主键(1是)"` + IsIncrement string `json:"isIncrement" description:"是否自增(1是)"` + IsRequired string `json:"isRequired" description:"是否必填(1是)"` + IsInsert string `json:"isInsert" description:"是否为插入字段(1是)"` + IsEdit string `json:"isEdit" description:"是否编辑字段(1是)"` + IsList string `json:"isList" description:"是否列表字段(1是)"` + IsQuery string `json:"isQuery" description:"是否查询字段(1是)"` + QueryType string `json:"queryType" description:"查询方式(等于、不等于、大于、小于、范围)"` + HtmlType string `json:"htmlType" description:"显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)"` + DictType string `json:"dictType" description:"字典类型"` + Sort int `json:"sort" description:"排序"` + LinkTableName string `json:"linkTableName" description:"关联表名"` + LinkTableClass string `json:"linkTableClass" description:"关联表类名"` + LinkTablePackage string `json:"linkTablePackage" description:"关联表包名"` + LinkLabelId string `json:"linkLabelId" description:"关联表键名"` + LinkLabelName string `json:"linkLabelName" description:"关联表字段值"` + CreateBy int `json:"createBy" description:"创建者"` + UpdateBy int `json:"updateBy" description:"更新者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} +type GenTableColumnEditInput struct { + Id int `json:"id" description:"ID"` + GenTableColumnAddInput +} + +// GenTableAndColumnsRes 表与字段组合数据 +type GenTableAndColumnsRes struct { + TableInfo *GenTableRes + Columns []*GenTableColumnRes `json:"columns"` +} diff --git a/internal/model/network_server.go b/internal/model/network_server.go index c512c27..af0140e 100644 --- a/internal/model/network_server.go +++ b/internal/model/network_server.go @@ -5,7 +5,7 @@ import "github.com/gogf/gf/v2/os/gtime" type NetworkServerOut struct { Id int `json:"id" description:""` Name string `json:"name" description:""` - Types string `json:"types" description:"tcp/udp/mqtt"` + Types string `json:"types" description:"tcp/udp/mqtt_server/http/websocket"` Addr string `json:"addr" description:""` Register string `json:"register" description:"注册包"` Heartbeat string `json:"heartbeat" description:"心跳包"` @@ -16,12 +16,21 @@ type NetworkServerOut struct { UpdatedAt *gtime.Time `json:"updatedAt" description:""` CreateBy int `json:"createBy" description:""` Remark string `json:"remark" description:"备注"` + + // 认证信息 + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick string `json:"stick" dc:"粘包处理方式"` } type NetworkServerRes struct { Id int `json:"id" description:""` Name string `json:"name" description:""` - Types string `json:"types" description:"tcp/udp/mqtt"` + Types string `json:"types" description:"tcp/udp/mqtt_server/http/websocket"` Addr string `json:"addr" description:""` Register string `json:"register" description:"注册包"` Heartbeat string `json:"heartbeat" description:"心跳包"` @@ -32,10 +41,19 @@ type NetworkServerRes struct { UpdatedAt *gtime.Time `json:"updatedAt" description:""` CreateBy int `json:"createBy" description:""` Remark string `json:"remark" description:"备注"` + + // 认证信息 + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick string `json:"stick" dc:"粘包处理方式"` } type NetworkServerAddInput struct { Name string `json:"name" description:""` - Types string `json:"types" description:"tcp/udp"` + Types string `json:"types" description:"tcp/udp/mqtt_server/http/websocket"` Addr string `json:"addr" description:""` Register string `json:"register" description:"注册包"` Heartbeat string `json:"heartbeat" description:"心跳包"` @@ -46,6 +64,15 @@ type NetworkServerAddInput struct { UpdatedAt *gtime.Time `json:"updatedAt" description:""` CreateBy int `json:"createBy" description:""` Remark string `json:"remark" description:"备注"` + + // 认证信息 + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick Stick `json:"stick" dc:"粘包处理方式"` } type NetworkServerEditInput struct { Id int `json:"id" description:"ID"` @@ -55,3 +82,15 @@ type NetworkServerEditInput struct { type GetNetworkServerListInput struct { PaginationInput } + +// 粘包处理方式 +type Stick struct { + Delimit string `json:"delimit,omitempty" dc:"分隔符"` + Custom string `json:"custom,omitempty" dc:"自定义脚本"` + FixedLen int `json:"fixedLen,omitempty" dc:"固定长度"` + Len struct { + Len int `json:"len" dc:"长度"` + Offset int `json:"offset" dc:"偏移量"` + Endian string `json:"endian" dc:"大小端(big|little)"` + } `json:"len,omitempty" dc:"长度字段"` +} diff --git a/internal/model/network_tunnel.go b/internal/model/network_tunnel.go index fd64920..6f3b422 100644 --- a/internal/model/network_tunnel.go +++ b/internal/model/network_tunnel.go @@ -55,7 +55,7 @@ type NetworkTunnelAddInput struct { Retry string `json:"retry" description:""` Heartbeat string `json:"heartbeat" description:""` Serial string `json:"serial" description:""` - Protoccol string `json:"protoccol" description:""` + Protocol string `json:"protocol" description:""` Status int `json:"status" description:""` Remark string `json:"remark" description:"备注"` } diff --git a/internal/model/notice_config.go b/internal/model/notice_config.go index c3b3b6c..5c1b7c4 100644 --- a/internal/model/notice_config.go +++ b/internal/model/notice_config.go @@ -11,6 +11,7 @@ type NoticeConfigListOutput struct { } type NoticeConfigOutput struct { Id string `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` Title string `json:"title" description:""` SendGateway string `json:"sendGateway" description:""` Types string `json:"types" description:""` diff --git a/internal/model/notice_log.go b/internal/model/notice_log.go index c77d9cd..cb750ae 100644 --- a/internal/model/notice_log.go +++ b/internal/model/notice_log.go @@ -1,18 +1,19 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/model/entity" ) type NoticeLogAddInput struct { - TemplateId string `json:"templateId" dc:"通知模板ID"` - SendGateway string `json:"sendGateway" dc:"通知发送通道:sms、work_weixin、dingding"` - Addressee string `json:"addressee" dc:"收信人"` - Title string `json:"title" dc:"通知标题"` - Content string `json:"content" dc:"通知内容"` - Status int `json:"status" dc:"发送状态:0=失败,1=成功"` - FailMsg string `json:"failMsg" dc:"失败信息"` - SendTime string `json:"sendTime" dc:"发送时间"` + TemplateId string `json:"templateId" dc:"通知模板ID"` + SendGateway string `json:"sendGateway" dc:"通知发送通道:sms、work_weixin、dingding"` + Addressee string `json:"addressee" dc:"收信人"` + Title string `json:"title" dc:"通知标题"` + Content string `json:"content" dc:"通知内容"` + Status int `json:"status" dc:"发送状态:0=失败,1=成功"` + FailMsg string `json:"failMsg" dc:"失败信息"` + SendTime *gtime.Time `json:"sendTime" dc:"发送时间"` } type NoticeLogSearchInput struct { diff --git a/internal/model/notice_template.go b/internal/model/notice_template.go index 31a8945..36c1b02 100644 --- a/internal/model/notice_template.go +++ b/internal/model/notice_template.go @@ -12,6 +12,7 @@ type NoticeTemplateListOutput struct { } type NoticeTemplateOutput struct { Id string `json:"id" description:""` + DeptId int `json:"deptId" description:"部门ID"` ConfigId string `json:"configId" description:""` SendGateway string `json:"sendGateway" description:""` Code string `json:"code" description:""` @@ -21,6 +22,7 @@ type NoticeTemplateOutput struct { } type NoticeTemplateAddInput struct { Id string `json:"id" description:"ID"` + DeptId int `json:"deptId" description:"部门ID"` SendGateway string `json:"sendGateway" description:""` Code string `json:"code" description:""` Title string `json:"title" description:""` diff --git a/internal/model/pg_sequences.go b/internal/model/pg_sequences.go new file mode 100644 index 0000000..87649d1 --- /dev/null +++ b/internal/model/pg_sequences.go @@ -0,0 +1,15 @@ +package model + +type PgSequenceOut struct { + SchemaName string `json:"schemaName" description:"模式名称"` + SeqUesCeName string `json:"seqUesCeName" description:"序号名称"` + SeqUesCeOwner int64 `json:"seqUesCeOwner" description:"所有者"` + DataType int64 `json:"dataType" description:"数据类型"` + StartVale int64 `json:"startVale" description:"开始值"` + MaxValue int64 `json:"maxValue" description:"最大值"` + MinValue int64 `json:"minValue" description:"最小值"` + IncrementBy int64 `json:"incrementBy" description:"自增量"` + Cycle int64 `json:"cycle" description:"是否启用循环"` + CacheSize int64 `json:"cacheSize" description:"缓存大小"` + LastVale int64 `json:"lastVale" description:"当前值"` +} diff --git a/internal/model/rule_instance.go b/internal/model/rule_instance.go new file mode 100644 index 0000000..7d3d94e --- /dev/null +++ b/internal/model/rule_instance.go @@ -0,0 +1,44 @@ +package model + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +type GetRuleInstanceListInput struct { + Types int `json:"types" description:"规则实例类型"` + *PaginationInput +} + +type RuleInstanceOut struct { + Id int `json:"id" description:"规则实例ID"` + DeptId int `json:"deptId" description:"部门ID"` + Name string `json:"name" description:"规则实例名称"` + Types int `json:"types" description:"规则实例类型"` + FlowId string `json:"flowId" description:"流程ID"` + Status int `json:"status" description:"状态"` + CreatedAt *gtime.Time `json:"createdAt" description:""` + UpdatedAt *gtime.Time `json:"updatedAt" description:""` + Expound string `json:"expound" description:"介绍"` +} + +type RuleInstanceRes struct { + Id int `json:"id" description:"规则实例ID"` + DeptId int `json:"deptId" description:"部门ID"` + Name string `json:"name" description:"规则实例名称"` + Types int `json:"types" description:"规则实例类型"` + FlowId string `json:"flowId" description:"流程ID"` + Status int `json:"status" description:"状态"` + CreatedAt *gtime.Time `json:"createdAt" description:""` + UpdatedAt *gtime.Time `json:"updatedAt" description:""` + Expound string `json:"expound" description:"介绍"` +} +type RuleInstanceAddInput struct { + Name string `json:"name" description:"规则实例名称"` + Types int `json:"types" description:"规则实例类型"` + Expound string `json:"expound" description:"介绍"` + FlowId string `json:"flowId" description:"流程ID"` +} +type RuleInstanceEditInput struct { + Id int `json:"id" description:"ID"` + RuleInstanceAddInput +} diff --git a/internal/model/statistics_data_overview.go b/internal/model/statistics_data_overview.go index 2338f52..7fe9887 100644 --- a/internal/model/statistics_data_overview.go +++ b/internal/model/statistics_data_overview.go @@ -1,10 +1,41 @@ package model +type DataOverviewRes struct { + CityNO string `json:"cityNO" description:"城市编号"` + Area float64 `json:"area" description:"供热面积"` + AllArea float64 `json:"allArea" description:"总面积"` + Calorie float64 `json:"calorie" description:"总耗热"` + SingleCalorie float64 `json:"singleCalorie" description:"热总单耗"` + Electric float64 `json:"electric" description:"总耗电"` + SingleElectric float64 `json:"singleElectric" description:"电总单耗"` + Water float64 `json:"water" description:"总耗水"` + SingleWater float64 `json:"singleWater" description:"水总单耗"` +} + +type BrokenLineRes struct { + Calorie []*BrokenLineChildRes `json:"calorie" description:"总热耗"` + Electric []*BrokenLineChildRes `json:"electric" description:"总电耗"` + Water []*BrokenLineChildRes `json:"water" description:"总水耗"` +} + type BrokenLineChildRes struct { AccessDay string `json:"accessDay" description:"日期"` Values float64 `json:"values" description:"数值"` } +type BarChartRes struct { + HuanLuNo string `json:"huanLuNo" description:"换热站编号"` + HuanLuName string `json:"huanLuName" description:"换热站名称"` + InPressure1 float64 `json:"inPressure1" description:"一网供水压力"` + InPressure2 float64 `json:"inPressure2" description:"二网供水压力"` + InTemperature1 float64 `json:"inTemperature1" description:"一网供水温度"` + InTemperature2 float64 `json:"inTemperature2" description:"二网供水温度"` + OutPressure1 float64 `json:"outPressure1" description:"一网回水压力"` + OutPressure2 float64 `json:"outPressure2" description:"二网回水压力"` + OutTemperature1 float64 `json:"outTemperature1" description:"一网回水温度"` + OutTemperature2 float64 `json:"outTemperature2" description:"二网回水温度"` +} + type TemperingRatioRes struct { TemperatureRange string `json:"temperatureRange" description:"温度区间"` Num string `json:"num" description:"区间数据量"` @@ -13,11 +44,16 @@ type TemperingRatioRes struct { // 物联概览统计数据 type ThingOverviewOutput struct { - Overview DeviceTotalOutput `json:"overview" dc:"物联概览统计数据"` - Device ThingDevice `json:"device" dc:"设备月度统计"` - AlarmLevel []AlarmLogLevelTotal `json:"alarmLevel" dc:"告警日志级别统计"` + Overview DeviceTotalOutput `json:"overview" dc:"物联概览统计数据"` + Device ThingDevice `json:"device" dc:"设备月度统计"` + DeviceForDay ThingDeviceForDay `json:"deviceForDay" dc:"设备近一个月统计"` + AlarmLevel []AlarmLogLevelTotal `json:"alarmLevel" dc:"告警日志级别统计"` } type ThingDevice struct { MsgTotal map[int]int `json:"msgTotal" dc:"设备消息量月度统计"` AlarmTotal map[int]int `json:"alarmTotal" dc:"设备告警量月度统计"` } +type ThingDeviceForDay struct { + MsgTotal map[string]int `json:"msgTotal" dc:"设备消息量近一个月统计"` + AlarmTotal map[string]int `json:"alarmTotal" dc:"设备告警量近一个月统计"` +} diff --git a/internal/model/sys_api.go b/internal/model/sys_api.go index 25ab9fe..1b8d0d8 100644 --- a/internal/model/sys_api.go +++ b/internal/model/sys_api.go @@ -5,11 +5,13 @@ type SysApiTreeRes struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` Status int `json:"status" description:"状态 0 停用 1启用"` Sort int `json:"sort" description:"排序"` + MenuIds []int `json:"menuIds" description:"菜单Id数组"` Children []*SysApiTreeRes `json:"children" description:"子集"` } @@ -18,11 +20,13 @@ type SysApiTreeOut struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` Status int `json:"status" description:"状态 0 停用 1启用"` Sort int `json:"sort" description:"排序"` + MenuIds []int `json:"menuIds" description:"菜单Id数组"` Children []*SysApiTreeOut `json:"children" description:"子集"` } @@ -31,6 +35,20 @@ type SysApiAllRes struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` + Method string `json:"method" description:"请求方式(数据字典维护)"` + Address string `json:"address" description:"接口地址"` + Remark string `json:"remark" description:"备注"` + Status int `json:"status" description:"状态 0 停用 1启用"` + Sort int `json:"sort" description:"排序"` +} + +type SysApiAllOut struct { + Id uint `json:"id" description:""` + ParentId int `json:"parentId" description:""` + Name string `json:"name" description:"名称"` + Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -43,6 +61,7 @@ type SysApiRes struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -56,12 +75,13 @@ type SysApiOut struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` Status int `json:"status" description:"状态 0 停用 1启用"` Sort int `json:"sort" description:"排序"` - MenuIds []int `json:"menuIds" description:"菜单Id数组" v:"required#菜单ID不能为空"` + MenuIds []int `json:"menuIds" description:"菜单Id数组"` } type AuthorizeQueryApiRes struct { @@ -71,6 +91,7 @@ type AuthorizeQueryApiRes struct { Title string `json:"title" description:"标题"` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -85,6 +106,7 @@ type AuthorizeQueryApiOut struct { Title string `json:"title" description:"标题"` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -98,6 +120,7 @@ type UserApiRes struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -111,6 +134,7 @@ type UserApiOut struct { ParentId int `json:"parentId" description:""` Name string `json:"name" description:"名称"` Types int `json:"types" description:"1 分类 2接口"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address" description:"接口地址"` Remark string `json:"remark" description:"备注"` @@ -122,6 +146,7 @@ type AddApiInput struct { ParentId int `json:"parentId"` Name string `json:"name"` Types int `json:"types"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address"` Remark string `json:"remark"` @@ -135,6 +160,7 @@ type EditApiInput struct { ParentId int `json:"parentId"` Name string `json:"name"` Types int `json:"types"` + ApiTypes string `json:"apiTypes" description:"数据字典维护"` Method string `json:"method" description:"请求方式(数据字典维护)"` Address string `json:"address"` Remark string `json:"remark"` @@ -142,3 +168,13 @@ type EditApiInput struct { Sort int `json:"sort"` MenuIds []int `json:"menuIds"` } + +type BindMenusReq struct { + Id int `json:"id"` + MenuIds []int `json:"menuIds"` +} + +type BindMenusInput struct { + Id int `json:"id" v:"required#API ID不能为空"` + MenuIds []int `json:"menuIds" v:"required#菜单ID不能为空"` +} diff --git a/internal/model/sys_authorize.go b/internal/model/sys_authorize.go new file mode 100644 index 0000000..0b70333 --- /dev/null +++ b/internal/model/sys_authorize.go @@ -0,0 +1,15 @@ +package model + +import "github.com/gogf/gf/v2/os/gtime" + +type SysAuthorizeInput struct { + RoleId int `json:"roleId" description:"角色ID"` + ItemsType string `json:"itemsType" description:"项目类型 menu菜单 button按钮 column列表字段 api接口"` + ItemsId int `json:"itemsId" description:"项目ID"` + IsCheckAll int `json:"isCheckAll" description:"是否全选 1是 0否"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建人"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/sys_certificate.go b/internal/model/sys_certificate.go new file mode 100644 index 0000000..121a448 --- /dev/null +++ b/internal/model/sys_certificate.go @@ -0,0 +1,82 @@ +package model + +import "github.com/gogf/gf/v2/os/gtime" + +type SysCertificateListRes struct { + Id int `json:"id" description:""` + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` + Status int `json:"status" description:"状态 0未启用 1启用"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} + +type SysCertificateListInput struct { + Name string `json:"name" description:"名称"` + Status int `json:"status" description:"状态 0未启用 1启用"` + PaginationInput +} + +type SysCertificateListOut struct { + Id int `json:"id" description:""` + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` + Status int `json:"status" description:"状态 0未启用 1启用"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} + +type SysCertificateOut struct { + Id int `json:"id" description:""` + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` + Status int `json:"status" description:"状态 0未启用 1启用"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} + +type AddSysCertificateListInput struct { + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` +} + +type EditSysCertificateListInput struct { + Id int `json:"id" description:""` + Name string `json:"name" description:"名称"` + Standard string `json:"standard" description:"证书标准"` + FileContent string `json:"fileContent" description:"证书文件内容"` + PublicKeyContent string `json:"publicKeyContent" description:"证书公钥内容"` + PrivateKeyContent string `json:"privateKeyContent" description:"证书私钥内容"` + Description string `json:"description" description:"说明"` +} diff --git a/internal/model/sys_dict_data.go b/internal/model/sys_dict_data.go index eb65f3c..61440e2 100644 --- a/internal/model/sys_dict_data.go +++ b/internal/model/sys_dict_data.go @@ -62,8 +62,8 @@ type SysDictDataOut struct { ListClass string `json:"listClass" description:"表格回显样式"` IsDefault int `json:"isDefault" description:"是否默认(1是 0否)"` Status int `json:"status" description:"状态(0正常 1停用)"` - CreateBy uint64 `json:"createBy" description:"创建者"` - UpdateBy uint64 `json:"updateBy" description:"更新者"` + CreatedBy uint64 `json:"createdBy" description:"创建者"` + UpdatedBy uint64 `json:"updatedBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` @@ -79,8 +79,8 @@ type SysDictDataRes struct { ListClass string `json:"listClass" description:"表格回显样式"` IsDefault int `json:"isDefault" description:"是否默认(1是 0否)"` Status int `json:"status" description:"状态(0正常 1停用)"` - CreateBy uint64 `json:"createBy" description:"创建者"` - UpdateBy uint64 `json:"updateBy" description:"更新者"` + CreatedBy uint64 `json:"createdBy" description:"创建者"` + UpdatedBy uint64 `json:"updatedBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改时间"` diff --git a/internal/model/sys_dict_type.go b/internal/model/sys_dict_type.go index 4f9c92b..4023872 100644 --- a/internal/model/sys_dict_type.go +++ b/internal/model/sys_dict_type.go @@ -6,17 +6,17 @@ type DictTypeDoInput struct { DictName string `p:"dictName"` //字典名称 DictType string `p:"dictType"` //字典类型 Status string `p:"status"` //字典状态 - ModuleClassify string `p:"moduleClassify"` //字典模块分类 + ModuleClassify string `p:"moduleClassify"` //模块分类 *PaginationInput } type SysDictTypeInfoOut struct { - DictId uint64 `orm:"dict_id,primary" json:"dictId"` // 字典主键 - DictName string `orm:"dict_name" json:"dictName"` // 字典名称 - DictType string `orm:"dict_type,unique" json:"dictType"` // 字典类型 - Status uint `orm:"status" json:"status"` // 状态(0正常 1停用) - Remark string `orm:"remark" json:"remark"` // 备注 - CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建日期 - ModuleClassify string `orm:"module_classify" json:"moduleClassify"` // 字典模块分类 + DictId uint64 `orm:"dict_id,primary" json:"dictId"` // 字典主键 + DictName string `orm:"dict_name" json:"dictName"` // 字典名称 + DictType string `orm:"dict_type,unique" json:"dictType"` // 字典类型 + Status uint `orm:"status" json:"status"` // 状态(0正常 1停用) + ModuleClassify string `orm:"moduleClassify" json:"moduleClassify"` //模块分类 + Remark string `orm:"remark" json:"remark"` // 备注 + CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建日期 } type AddDictTypeInput struct { @@ -24,7 +24,8 @@ type AddDictTypeInput struct { DictType string `p:"dictType"` Status uint `p:"status"` Remark string `p:"remark"` - ModuleClassify string `p:"moduleClassify"` + ModuleClassify string `p:"moduleClassify"` //模块分类 + } type EditDictTypeInput struct { @@ -33,17 +34,18 @@ type EditDictTypeInput struct { DictType string `p:"dictType"` Status uint `p:"status"` Remark string `p:"remark"` - ModuleClassify string `p:"moduleClassify"` + ModuleClassify string `p:"moduleClassify"` //模块分类 + } type SysDictTypeInfoRes struct { - DictId uint64 `orm:"dict_id,primary" json:"dictId"` // 字典主键 - DictName string `orm:"dict_name" json:"dictName"` // 字典名称 - DictType string `orm:"dict_type,unique" json:"dictType"` // 字典类型 - Status uint `orm:"status" json:"status"` // 状态(0正常 1停用) - Remark string `orm:"remark" json:"remark"` // 备注 - CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建日期 - ModuleClassify string `orm:"module_classify" json:"moduleClassify"` // 字典模块分类 + DictId uint64 `orm:"dict_id,primary" json:"dictId"` // 字典主键 + DictName string `orm:"dict_name" json:"dictName"` // 字典名称 + DictType string `orm:"dict_type,unique" json:"dictType"` // 字典类型 + Status uint `orm:"status" json:"status"` // 状态(0正常 1停用) + ModuleClassify string `orm:"moduleClassify" json:"moduleClassify"` //模块分类 + Remark string `orm:"remark" json:"remark"` // 备注 + CreatedAt *gtime.Time `orm:"created_at" json:"createdAt"` // 创建日期 } type SysDictTypeOut struct { @@ -51,12 +53,12 @@ type SysDictTypeOut struct { DictName string `json:"dictName" description:"字典名称"` DictType string `json:"dictType" description:"字典类型"` Status uint `json:"status" description:"状态(0正常 1停用)"` + ModuleClassify string `json:"moduleClassify" description:"模块分类"` CreateBy uint `json:"createBy" description:"创建者"` UpdateBy uint `json:"updateBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` - ModuleClassify string `json:"moduleClassify" description:"字典模块分类"` } type SysDictTypeRes struct { @@ -64,10 +66,10 @@ type SysDictTypeRes struct { DictName string `json:"dictName" description:"字典名称"` DictType string `json:"dictType" description:"字典类型"` Status uint `json:"status" description:"状态(0正常 1停用)"` + ModuleClassify string `json:"moduleClassify" description:"模块分类"` CreateBy uint `json:"createBy" description:"创建者"` UpdateBy uint `json:"updateBy" description:"更新者"` Remark string `json:"remark" description:"备注"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` - ModuleClassify string `json:"moduleClassify" description:"字典模块分类"` } diff --git a/internal/model/sys_job.go b/internal/model/sys_job.go index 5c96296..9a9ff34 100644 --- a/internal/model/sys_job.go +++ b/internal/model/sys_job.go @@ -28,6 +28,10 @@ type SysJobRes struct { UpdatedAt *gtime.Time `orm:"updated_at" json:"updatedAt"` // 更新时间 DeletedAt *gtime.Time `orm:"deleted_at" json:"deletedAt"` // 删除时间 } +type SysJobFunListOut struct { + FunName string `json:"fun_name"` + Explain string `json:"explain"` +} type SysJobOut struct { JobId int64 `orm:"job_id,primary" json:"jobId"` // 任务ID diff --git a/internal/model/sys_login_log.go b/internal/model/sys_login_log.go index 15b592b..c00b230 100644 --- a/internal/model/sys_login_log.go +++ b/internal/model/sys_login_log.go @@ -17,7 +17,7 @@ type SysLoginLogInput struct { LoginLocation string `json:"loginLocation" description:"登录地点"` Browser string `json:"browser" description:"浏览器类型"` Os string `json:"os" description:"操作系统"` - Status int `json:"status" description:"登录状态(0成功 1失败)"` + Status int `json:"status" description:"登录状态(0失败 1成功)"` Msg string `json:"msg" description:"提示消息"` LoginTime *gtime.Time `json:"loginTime" description:"登录时间"` Module string `json:"module" description:"登录模块"` @@ -36,7 +36,20 @@ type SysLoginLogOut struct { LoginLocation string `json:"loginLocation" description:"登录地点"` Browser string `json:"browser" description:"浏览器类型"` Os string `json:"os" description:"操作系统"` - Status int `json:"status" description:"登录状态(0成功 1失败)"` + Status int `json:"status" description:"登录状态(0失败 1成功)"` + Msg string `json:"msg" description:"提示消息"` + LoginTime *gtime.Time `json:"loginTime" description:"登录时间"` + Module string `json:"module" description:"登录模块"` +} + +type SysLoginLogExportOut struct { + InfoId int64 `json:"infoId" description:"访问ID"` + LoginName string `json:"loginName" description:"登录账号"` + Ipaddr string `json:"ipaddr" description:"登录IP地址"` + LoginLocation string `json:"loginLocation" description:"登录地点"` + Browser string `json:"browser" description:"浏览器类型"` + Os string `json:"os" description:"操作系统"` + Status string `json:"status" description:"登录状态"` Msg string `json:"msg" description:"提示消息"` LoginTime *gtime.Time `json:"loginTime" description:"登录时间"` Module string `json:"module" description:"登录模块"` diff --git a/internal/model/sys_menu_api.go b/internal/model/sys_menu_api.go index daa6c08..77c9351 100644 --- a/internal/model/sys_menu_api.go +++ b/internal/model/sys_menu_api.go @@ -15,3 +15,13 @@ type AddMenuApiReq struct { CreatedBy uint `json:"createdBy" description:"创建人"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` } + +type SysMenuApiInput struct { + MenuId int `json:"menuId" description:"菜单ID"` + ApiId int `json:"apiId" description:"apiId"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建人"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/sys_message.go b/internal/model/sys_message.go new file mode 100644 index 0000000..d479c54 --- /dev/null +++ b/internal/model/sys_message.go @@ -0,0 +1,52 @@ +package model + +import ( + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/model/entity" +) + +type MessageListDoInput struct { + Types int `json:"types" description:"消息类型"` + Title string `json:"title" description:"标题"` + *PaginationInput +} + +type MessageListRes struct { + Id int `json:"id" description:""` + UserId int `json:"userId" description:"用户ID"` + MessageId int `json:"messageId" description:"消息ID"` + IsRead int `json:"isRead" description:"是否已读 0 未读 1已读"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + ReadTime *gtime.Time `json:"readTime" description:"阅读时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` + MessageInfo *entity.SysMessage `orm:"with:id=message_id" description:"消息"` +} + +type MessageListOut struct { + Id int `json:"id" description:""` + UserId int `json:"userId" description:"用户ID"` + MessageId int `json:"messageId" description:"消息ID"` + IsRead int `json:"isRead" description:"是否已读 0 未读 1已读"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + ReadTime *gtime.Time `json:"readTime" description:"阅读时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` + MessageInfo *entity.SysMessage `orm:"with:id=message_id" description:"消息"` +} + +type AddMessageInput struct { + Title string `json:"title" description:"标题"` + Types int `json:"types" description:"消息类型 字典表中查询具体消息类型"` + Scope int `json:"scope" description:"消息范围 字典表中查看具体消息范围"` + Content string `json:"content" description:"内容"` + ObjectId int `json:"objectId" description:"推送对象ID"` +} + +type SysMessagereceiveInput struct { + UserId int `json:"userId" description:"用户ID"` + MessageId int `json:"messageId" description:"消息ID"` + IsRead int `json:"isRead" description:"是否已读 0 未读 1已读"` + IsPush int `json:"isPush" description:"是否已经推送0 否 1是"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + ReadTime *gtime.Time `json:"readTime" description:"阅读时间"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} diff --git a/internal/model/sys_plugins.go b/internal/model/sys_plugins.go index a9facea..a401611 100644 --- a/internal/model/sys_plugins.go +++ b/internal/model/sys_plugins.go @@ -1,33 +1,129 @@ package model +import "github.com/gogf/gf/v2/os/gtime" + +type GetSysPluginsListRes struct { + Id int `json:"id" description:"ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` + HandleType string `json:"handleType" description:"功能类型"` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args string `json:"args" description:"插件的指令参数"` + Status int `json:"status" description:"状态"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` + StartTime *gtime.Time `json:"startTime" description:"启动时间"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} + +type GetSysPluginsListOut struct { + Id int `json:"id" description:"ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` + HandleType string `json:"handleType" description:"功能类型"` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args string `json:"args" description:"插件的指令参数"` + Status int `json:"status" description:"状态"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` + StartTime *gtime.Time `json:"startTime" description:"启动时间"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` +} + type GetSysPluginsListInput struct { + Status int `json:"status" description:"状态"` PaginationInput } -type SysPluginsOutput struct { - Intro string `json:"intro" description:"介绍"` - Status int `json:"status" description:"状态"` - Types string `json:"types" description:"插件类型"` - StartTime string `json:"startTime" description:""` - Id int `json:"id" description:"ID"` - Name string `json:"name" description:"名称"` - Title string `json:"title" description:"标题"` - Version string `json:"version" description:"版本"` - Author string `json:"author" description:""` +type SysPluginsRes struct { + Id int `json:"id" description:"ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` + HandleType string `json:"handleType" description:"功能类型"` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args string `json:"args" description:"插件的指令参数"` + Status int `json:"status" description:"状态"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` + StartTime *gtime.Time `json:"startTime" description:"启动时间"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy int `json:"updatedBy" description:"修改人"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` + DeletedBy int `json:"deletedBy" description:"删除人"` + DeletedAt *gtime.Time `json:"deletedAt" description:"删除时间"` } + type SysPluginsAddInput struct { - Version string `json:"version" description:"版本"` - Author string `json:"author" description:""` - Status int `json:"status" description:"状态"` - Types string `json:"types" description:"插件类型"` - StartTime string `json:"startTime" description:""` - Name string `json:"name" description:"名称"` - Title string `json:"title" description:"标题"` - Intro string `json:"intro" description:"介绍"` + Id int `json:"id" description:"ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` + HandleType string `json:"handleType" description:"功能类型"` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args string `json:"args" description:"插件的指令参数"` + Status int `json:"status" description:"状态"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` + StartTime *gtime.Time `json:"startTime" description:"启动时间"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` } type SysPluginsEditInput struct { - Id int `json:"id" description:"ID"` - SysPluginsAddInput + Id int `json:"id" description:"ID"` + Types string `json:"types" description:"插件与SagooIOT的通信方式"` + HandleType string `json:"handleType" description:"功能类型"` + Name string `json:"name" description:"名称"` + Title string `json:"title" description:"标题"` + Description string `json:"description" description:"介绍"` + Version string `json:"version" description:"版本"` + Author string `json:"author" description:"作者"` + Icon string `json:"icon" description:"插件图标"` + Link string `json:"link" description:"插件的网址。指向插件的 github 链接。值应为一个可访问的网址"` + Command string `json:"command" description:"插件的运行指令"` + Args []string `json:"args" description:"插件的指令参数"` + FrontendUi int `json:"frontendUi" description:"是否有插件页面"` + FrontendUrl string `json:"frontendUrl" description:"插件页面地址"` + FrontendConfiguration int `json:"frontendConfiguration" description:"是否显示配置页面"` } type SysPluginsInfoRes struct { diff --git a/internal/model/sys_post.go b/internal/model/sys_post.go index adc66df..1a174ab 100644 --- a/internal/model/sys_post.go +++ b/internal/model/sys_post.go @@ -56,3 +56,15 @@ type DetailPostRes struct { IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` } + +type DetailPostOut struct { + PostId int64 `json:"postId" description:"岗位ID"` + ParentId int64 `json:"parentId" description:"父ID"` + PostCode string `json:"postCode" description:"岗位编码"` + PostName string `json:"postName" description:"岗位名称"` + PostSort int `json:"postSort" description:"显示顺序"` + Status uint `json:"status" description:"状态(0正常 1停用)"` + Remark string `json:"remark" description:"备注"` + IsDeleted int `json:"isDeleted" description:"是否删除 0未删除 1已删除"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` +} diff --git a/internal/model/sys_role.go b/internal/model/sys_role.go index e3ead9f..bcd4b6e 100644 --- a/internal/model/sys_role.go +++ b/internal/model/sys_role.go @@ -10,9 +10,9 @@ type RoleTreeRes struct { DataScope uint `json:"dataScope" description:"数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)"` Remark string `json:"remark" description:"备注"` Status uint `json:"status" description:"状态;0:禁用;1:正常"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` Children []*RoleTreeRes `json:"children" description:"子集"` } @@ -25,9 +25,9 @@ type RoleTreeOut struct { DataScope uint `json:"dataScope" description:"数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)"` Remark string `json:"remark" description:"备注"` Status uint `json:"status" description:"状态;0:禁用;1:正常"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` Children []*RoleTreeOut `json:"children" description:"子集"` } @@ -58,8 +58,23 @@ type RoleInfoRes struct { DeptIds []int64 `json:"deptIds" description:"数据范围为自定义数据权限时返回部门ID数组"` Remark string `json:"remark" description:"备注"` Status uint `json:"status" description:"状态;0:禁用;1:正常"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` +} + +type RoleInfoOut struct { + Id uint `json:"id" description:""` + ParentId int `json:"parentId" description:"父ID"` + ListOrder uint `json:"listOrder" description:"排序"` + Name string `json:"name" description:"角色名称"` + DataScope uint `json:"dataScope" description:"数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)"` + DeptIds []int64 `json:"deptIds" description:"数据范围为自定义数据权限时返回部门ID数组"` + Remark string `json:"remark" description:"备注"` + Status uint `json:"status" description:"状态;0:禁用;1:正常"` + CreatedBy uint `json:"createdBy" description:"创建者"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` } diff --git a/internal/model/sys_user.go b/internal/model/sys_user.go index 5a321bb..2962bbb 100644 --- a/internal/model/sys_user.go +++ b/internal/model/sys_user.go @@ -30,7 +30,7 @@ type UserListOut struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号"` UserNickname string `json:"userNickname" description:"用户昵称"` - Birthday int `json:"birthday" description:"生日"` + Birthday *gtime.Time `json:"birthday" description:"生日"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` Avatar string `json:"avatar" description:"用户头像"` @@ -42,9 +42,9 @@ type UserListOut struct { LastLoginIp string `json:"lastLoginIp" description:"最后登录ip"` LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` Dept *DetailDeptRes `json:"dept" description:"部门信息"` RolesNames string `json:"rolesNames" description:"角色信息"` @@ -56,7 +56,7 @@ type UserListRes struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号"` UserNickname string `json:"userNickname" description:"用户昵称"` - Birthday int `json:"birthday" description:"生日"` + Birthday *gtime.Time `json:"birthday" description:"生日"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` Avatar string `json:"avatar" description:"用户头像"` @@ -68,9 +68,9 @@ type UserListRes struct { LastLoginIp string `json:"lastLoginIp" description:"最后登录ip"` LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` Dept *DetailDeptRes `json:"dept" description:"部门信息"` RolesNames string `json:"rolesNames" description:"角色信息"` @@ -82,7 +82,7 @@ type UserRes struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号"` UserNickname string `json:"userNickname" description:"用户昵称"` - Birthday int `json:"birthday" description:"生日"` + Birthday *gtime.Time `json:"birthday" description:"生日"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` Avatar string `json:"avatar" description:"用户头像"` @@ -94,9 +94,9 @@ type UserRes struct { LastLoginIp string `json:"lastLoginIp" description:"最后登录ip"` LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` Dept *DetailDeptRes `json:"dept" description:"部门信息"` RolesNames string `json:"rolesNames" description:"角色信息"` @@ -107,7 +107,7 @@ type AddUserInput struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号" v:"required#手机号不能为空"` UserNickname string `json:"userNickname" description:"用户昵称" v:"required#用户昵称不能为空"` - Birthday int `json:"birthday" description:"生日"` + Birthday string `json:"birthday" description:"生日"` UserPassword string `json:"userPassword" description:"登录密码;cmf_password加密"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` @@ -148,7 +148,7 @@ type UserInfoRes struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号"` UserNickname string `json:"userNickname" description:"用户昵称"` - Birthday int `json:"birthday" description:"生日"` + Birthday *gtime.Time `json:"birthday" description:"生日"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` Avatar string `json:"avatar" description:"用户头像"` @@ -160,9 +160,9 @@ type UserInfoRes struct { LastLoginIp string `json:"lastLoginIp" description:"最后登录ip"` LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` RoleIds []int `json:"roleIds" description:"角色ID数组" v:"required#角色不能为空"` PostIds []int `json:"postIds" description:"岗位ID数组" v:"required#岗位不能为空"` @@ -173,7 +173,7 @@ type UserInfoOut struct { UserTypes string `json:"userTypes" description:"系统 system 企业 company"` Mobile string `json:"mobile" description:"中国手机不带国家代码,国际手机号格式为:国家代码-手机号"` UserNickname string `json:"userNickname" description:"用户昵称"` - Birthday int `json:"birthday" description:"生日"` + Birthday *gtime.Time `json:"birthday" description:"生日"` UserEmail string `json:"userEmail" description:"用户登录邮箱"` Sex int `json:"sex" description:"性别;0:保密,1:男,2:女"` Avatar string `json:"avatar" description:"用户头像"` @@ -185,9 +185,9 @@ type UserInfoOut struct { LastLoginIp string `json:"lastLoginIp" description:"最后登录ip"` LastLoginTime *gtime.Time `json:"lastLoginTime" description:"最后登录时间"` Status uint `json:"status" description:"用户状态;0:禁用,1:正常,2:未验证"` - CreateBy uint `json:"createBy" description:"创建者"` + CreatedBy uint `json:"createdBy" description:"创建者"` CreatedAt *gtime.Time `json:"createdAt" description:"创建日期"` - UpdateBy uint `json:"updateBy" description:"更新者"` + UpdatedBy uint `json:"updatedBy" description:"更新者"` UpdatedAt *gtime.Time `json:"updatedAt" description:"修改日期"` RoleIds []int `json:"roleIds" description:"角色ID数组" v:"required#角色不能为空"` PostIds []int `json:"postIds" description:"岗位ID数组" v:"required#岗位不能为空"` diff --git a/internal/mqtt/mqtt.go b/internal/mqtt/mqtt.go index 11cc360..76a1099 100644 --- a/internal/mqtt/mqtt.go +++ b/internal/mqtt/mqtt.go @@ -2,18 +2,17 @@ package mqtt import ( "context" + "encoding/json" MQTT "github.com/eclipse/paho.mqtt.golang" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gctx" - "github.com/sagoo-cloud/sagooiot/network/pkg/mqttclient" + "sagooiot/pkg/mqttclient" ) -var systemMqttClient *mqttclient.MqttWrapperClient - func InitSystemMqtt() error { var ctx = gctx.New() var err error - systemMqttClient, err = mqttclient.InitMqtt(&mqttclient.MqttConf{ + err = mqttclient.InitMqtt(&mqttclient.MqttConf{ Addr: g.Cfg().MustGet(ctx, "mqtt.addr").String(), ClientId: g.Cfg().MustGet(ctx, "mqtt.clientId").String(), UserName: g.Cfg().MustGet(ctx, "mqtt.auth.userName").String(), @@ -23,15 +22,24 @@ func InitSystemMqtt() error { } func Close() { - systemMqttClient.Close() + mqttclient.Close() } func Publish(topic string, payload []byte) error { - return systemMqttClient.Publish(topic, payload) + return mqttclient.Publish(topic, payload) +} + +func PublishWithInterface(topic string, payload interface{}) error { + data, marshalErr := json.Marshal(payload) + if marshalErr != nil { + return marshalErr + } + return mqttclient.Publish(topic, data) } +// Subscribe todo 全局应该只有一个处理,需要设置消息隔离级别 func Subscribe(ctx context.Context, topic string, f func(context.Context, MQTT.Client, MQTT.Message)) error { - return systemMqttClient.Subscribe(ctx, topic, func(client MQTT.Client, message MQTT.Message) { + return mqttclient.Subscribe(ctx, topic, func(client MQTT.Client, message MQTT.Message) { f(ctx, client, message) }) } diff --git a/internal/queues/base.go b/internal/queues/base.go new file mode 100644 index 0000000..5b6a2bf --- /dev/null +++ b/internal/queues/base.go @@ -0,0 +1,8 @@ +package queues + +func Run() { + ScheduledDeviceAlarmLogRun() + ScheduledSysOperLogRun() + TaskDeviceDataTsdSaveRun() + DeviceInfoUpdateRun() +} diff --git a/internal/queues/deviceAlarmLog.go b/internal/queues/deviceAlarmLog.go new file mode 100644 index 0000000..3bc0e1e --- /dev/null +++ b/internal/queues/deviceAlarmLog.go @@ -0,0 +1,41 @@ +package queues + +import ( + "context" + "encoding/json" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/worker" +) + +var ScheduledDeviceAlarmLog = new(worker.Scheduled) + +func ScheduledDeviceAlarmLogRun() { + ScheduledDeviceAlarmLog = worker.RegisterProcess(DeviceAlarmLog) +} + +// DeviceAlarmLog 设备告警日志 +var DeviceAlarmLog = &qDeviceAlarmLog{} + +type qDeviceAlarmLog struct{} + +// GetTopic 主题 +func (q *qDeviceAlarmLog) GetTopic() string { + return consts.QueueDeviceAlarmLogTopic +} + +// Handle 处理消息 +func (q *qDeviceAlarmLog) Handle(ctx context.Context, p worker.Payload) (err error) { + if p.Payload == nil || q.GetTopic() != p.Group { + return nil + } + var data model.AlarmLogAddInput + if err = json.Unmarshal(p.Payload, &data); err != nil { + return err + } + //真正写日志 + _, err = service.AlarmLog().Add(ctx, &data) + + return +} diff --git a/internal/queues/deviceDataTsdSave.go b/internal/queues/deviceDataTsdSave.go new file mode 100644 index 0000000..da25c27 --- /dev/null +++ b/internal/queues/deviceDataTsdSave.go @@ -0,0 +1,97 @@ +package queues + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "sagooiot/pkg/channelx" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/tsd" + "sagooiot/pkg/worker" + "time" +) + +var DeviceDataSaveWorker = new(worker.Scheduled) +var deviceDataSaveAggregator *channelx.Aggregator //批量处理器 + +// TaskDeviceDataTsdSaveRun 设备数据保存到时序数据 +func TaskDeviceDataTsdSaveRun() { + DeviceDataSaveWorker = worker.RegisterProcess(DeviceDataSave) + + batchSize := g.Cfg().MustGet(context.Background(), "tsd.aggregator.batchSize", 1000).Int() //批处理大小 + workers := g.Cfg().MustGet(context.Background(), "tsd.aggregator.workers", 100).Int() //工作线程数 + channelBufferSize := g.Cfg().MustGet(context.Background(), "tsd.aggregator.channelBufferSize", 50000).Int() //通道缓冲区大小 + lingerTime := g.Cfg().MustGet(context.Background(), "tsd.aggregator.lingerTime", 100).Int() //防止数据积压时间 + + // 创建聚合器实例 + deviceDataSaveAggregator = channelx.NewAggregator( + deviceDataSaveBatchProcessFunc, + channelx.WithBatchSize(batchSize), + channelx.WithWorkers(workers), + channelx.WithChannelBufferSize(channelBufferSize), + channelx.WithLingerTime(time.Duration(lingerTime)*time.Millisecond), + channelx.WithLogger(nil), + ) + // 开始聚合器 + deviceDataSaveAggregator.Start() +} + +var DeviceDataSave = &qDeviceDataSave{} + +type qDeviceDataSave struct{} + +// GetTopic 主题 +func (q *qDeviceDataSave) GetTopic() string { + return consts.QueueDeviceDataSaveTopic +} + +// Handle 处理消息 +func (q *qDeviceDataSave) Handle(ctx context.Context, p worker.Payload) (err error) { + if p.Payload == nil || q.GetTopic() != p.Group { + return nil + } + var deviceLog = iotModel.DeviceLog{} + if err := json.Unmarshal(p.Payload, &deviceLog); err != nil { + g.Log().Debugf(ctx, "DeviceDataSaveWorker Failed to unmarshal data: %v", err) + } + + //数据进入到批量操作,等待批量处理 + if !deviceDataSaveAggregator.EnqueueWithRetry(deviceLog, 2, 100*time.Millisecond) { + g.Log().Debug(ctx, "Failed to enqueue item: ", deviceLog) + } + + return +} + +// deviceDataSaveBatchProcessFunc 批处理函数 +func deviceDataSaveBatchProcessFunc(items []interface{}) error { + // 创建数据库连接 + db := tsd.GetDB() + defer db.Close() + deviceDataList := make(map[string][]iotModel.ReportPropertyData) + for _, item := range items { + var devLog = iotModel.DeviceLog{} + err := gconv.Scan(item, &devLog) + if err != nil { + return err + } + + // 基于物模型解析数据 + if devLog.Type == consts.MsgTypePropertyReport { + deviceData, err := service.DevTSLParse().ParseData(context.Background(), devLog.Device, []byte(devLog.Content)) + if err != nil { + g.Log().Debug(context.Background(), "解析设备日志数据失败:", err, devLog.Content) + continue + } + deviceDataList[devLog.Device] = append(deviceDataList[devLog.Device], deviceData) + } + } + _, err := db.BatchInsertMultiDeviceData(deviceDataList) + if err != nil { + g.Log().Debug(context.Background(), "批量插入设备日志数据失败:", err) + } + return nil +} diff --git a/internal/queues/deviceInfoUpdate.go b/internal/queues/deviceInfoUpdate.go new file mode 100644 index 0000000..f96c9d1 --- /dev/null +++ b/internal/queues/deviceInfoUpdate.go @@ -0,0 +1,76 @@ +package queues + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "sagooiot/pkg/channelx" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/worker" + "time" +) + +var DeviceStatusInfoUpdateWorker = new(worker.Scheduled) +var deviceInfoUpdateAggregator *channelx.Aggregator //批量处理器 + +// DeviceInfoUpdateRun 更新设备状态信息,设备上线、离线、注册,更新数据库 +func DeviceInfoUpdateRun() { + DeviceStatusInfoUpdateWorker = worker.RegisterProcess(DeviceStatusInfoUpdate) + + batchSize := 200 // 批处理大小 + workers := 200 + channelBufferSize := 50000 // 通道缓冲区大小 + lingerTime := 100 * time.Millisecond + + // 创建聚合器实例 + deviceInfoUpdateAggregator = channelx.NewAggregator( + deviceInfoUpdateBatchProcessFunc, + channelx.WithBatchSize(batchSize), + channelx.WithWorkers(workers), + channelx.WithChannelBufferSize(channelBufferSize), + channelx.WithLingerTime(lingerTime), + channelx.WithLogger(nil), + ) + // 开始聚合器 + deviceInfoUpdateAggregator.Start() +} + +// DeviceStatusInfoUpdate 系统日志 +var DeviceStatusInfoUpdate = &qDeviceStatusInfoUpdate{} + +type qDeviceStatusInfoUpdate struct{} + +// GetTopic 主题 +func (q *qDeviceStatusInfoUpdate) GetTopic() string { + return consts.QueueDeviceStatusInfoUpdate +} + +// Handle 处理消息 +func (q *qDeviceStatusInfoUpdate) Handle(ctx context.Context, p worker.Payload) (err error) { + if p.Payload == nil || q.GetTopic() != p.Group { + return nil + } + var data iotModel.DeviceStatusLog + if err = json.Unmarshal(p.Payload, &data); err != nil { + return err + } + //数据进入到批量操作,等待批量处理 + if !deviceInfoUpdateAggregator.EnqueueWithRetry(data, 3, 100*time.Millisecond) { + g.Log().Debug(ctx, "Failed to enqueue item: ", data) + } + return +} + +// deviceInfoUpdateBatchProcessFunc 批处理函数 +func deviceInfoUpdateBatchProcessFunc(items []interface{}) (err error) { + var data []iotModel.DeviceStatusLog + err = gconv.Scan(items, &data) + err = service.DevDevice().BatchUpdateDeviceStatusInfo(context.Background(), data) + if err != nil { + return err + } + return nil +} diff --git a/internal/queues/sysOperLog.go b/internal/queues/sysOperLog.go new file mode 100644 index 0000000..4eb86d8 --- /dev/null +++ b/internal/queues/sysOperLog.go @@ -0,0 +1,39 @@ +package queues + +import ( + "context" + "encoding/json" + "sagooiot/internal/consts" + "sagooiot/internal/model/entity" + "sagooiot/internal/service" + "sagooiot/pkg/worker" +) + +var ScheduledSysOperLog = new(worker.Scheduled) + +func ScheduledSysOperLogRun() { + ScheduledSysOperLog = worker.RegisterProcess(SysOperLog) +} + +// SysOperLog 系统日志 +var SysOperLog = &qSysOperLog{} + +type qSysOperLog struct{} + +// GetTopic 主题 +func (q *qSysOperLog) GetTopic() string { + return consts.QueueRequestLogTopic +} + +// Handle 处理消息 +func (q *qSysOperLog) Handle(ctx context.Context, p worker.Payload) (err error) { + if p.Payload == nil || q.GetTopic() != p.Group { + return nil + } + var data entity.SysOperLog + if err = json.Unmarshal(p.Payload, &data); err != nil { + return err + } + //真正写日志 + return service.SysOperLog().RealWrite(ctx, data) +} diff --git a/internal/service/.gitkeep b/internal/service/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/internal/service/alarm.go b/internal/service/alarm.go index f00cb08..92a3506 100644 --- a/internal/service/alarm.go +++ b/internal/service/alarm.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,24 +7,10 @@ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type ( - IAlarmRule interface { - List(ctx context.Context, in *model.AlarmRuleListInput) (out *model.AlarmRuleListOutput, err error) - Cache(ctx context.Context) (rs map[string][]model.AlarmRuleOutput, err error) - Detail(ctx context.Context, id uint64) (out *model.AlarmRuleOutput, err error) - Add(ctx context.Context, in *model.AlarmRuleAddInput) (err error) - Edit(ctx context.Context, in *model.AlarmRuleEditInput) (err error) - Deploy(ctx context.Context, id uint64) (err error) - Undeploy(ctx context.Context, id uint64) (err error) - Del(ctx context.Context, id uint64) (err error) - Operator(ctx context.Context) (out []model.OperatorOutput, err error) - TriggerType(ctx context.Context, productKey string) (out []model.TriggerTypeOutput, err error) - TriggerParam(ctx context.Context, productKey string, triggerType int) (out []model.TriggerParamOutput, err error) - Check(ctx context.Context, productKey string, deviceKey string, triggerType int, data map[string]any) (err error) - } IAlarmLevel interface { Detail(ctx context.Context, level uint) (out model.AlarmLevelOutput, err error) All(ctx context.Context) (out *model.AlarmLevelListOutput, err error) @@ -36,8 +22,35 @@ type ( List(ctx context.Context, in *model.AlarmLogListInput) (out *model.AlarmLogListOutput, err error) Handle(ctx context.Context, in *model.AlarmLogHandleInput) (err error) TotalForLevel(ctx context.Context) (total []model.AlarmLogLevelTotal, err error) + // ClearLogByDays 按日期删除日志 ClearLogByDays(ctx context.Context, days int) (err error) } + IAlarmRule interface { + List(ctx context.Context, in *model.AlarmRuleListInput) (out *model.AlarmRuleListOutput, err error) + // TODO 废弃 + Cache(ctx context.Context) (rs map[string][]model.AlarmRuleOutput, err error) + // CacheAllAlarmRule 缓存所有的告警规则 + CacheAllAlarmRule(ctx context.Context) (err error) + // Detail 获取告警规则详情 + Detail(ctx context.Context, id uint64) (out *model.AlarmRuleOutput, err error) + Add(ctx context.Context, in *model.AlarmRuleAddInput) (err error) + Edit(ctx context.Context, in *model.AlarmRuleEditInput) (err error) + // Deploy 启用告警规则 + Deploy(ctx context.Context, id uint64) (err error) + Undeploy(ctx context.Context, id uint64) (err error) + Del(ctx context.Context, id uint64) (err error) + Operator(ctx context.Context) (out []model.OperatorOutput, err error) + TriggerType(ctx context.Context, productKey string) (out []model.TriggerTypeOutput, err error) + TriggerParam(ctx context.Context, productKey string, triggerType int, eventKey ...string) (out []model.TriggerParamOutput, err error) + // NoticeAction 执行告警通知 + NoticeAction(ctx context.Context, rule model.AlarmRuleOutput, expression string, deviceKey string, param any) + // Check 告警检测 + Check(ctx context.Context, productKey string, deviceKey string, triggerType int, param any, subKey ...string) (err error) + // AddCronRule 添加定时触发规则 + AddCronRule(ctx context.Context, in *model.AlarmCronRuleAddInput) (err error) + // EditCronRule 编辑定时触发规则 + EditCronRule(ctx context.Context, in *model.AlarmCronRuleEditInput) (err error) + } ) var ( diff --git a/internal/service/analysis.go b/internal/service/analysis.go new file mode 100644 index 0000000..5acfff0 --- /dev/null +++ b/internal/service/analysis.go @@ -0,0 +1,117 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + "sagooiot/internal/model" + "sagooiot/pkg/general" +) + +type ( + IAnalysisDeviceDataTsd interface { + GetDeviceData(ctx context.Context, reqData general.SelectReq) (rs []interface{}, err error) + } + IAnalysisProduct interface { + // GetDeviceCountForProduct 获取产品下的设备数量 + GetDeviceCountForProduct(ctx context.Context, productKey string) (number int, err error) + // GetProductCount 获取产品数量统计 + GetProductCount(ctx context.Context) (res model.ProductCountRes, err error) + } + IAnalysisAlarm interface { + // GetDeviceAlertCountByYearMonth 按年度每月设备告警数统计 + GetDeviceAlertCountByYearMonth(ctx context.Context, year string) (res []model.CountData, err error) + // GetDeviceAlertCountByMonthDay 按月度每日设备告警数统计 + GetDeviceAlertCountByMonthDay(ctx context.Context, month string) (res []model.CountData, err error) + // GetDeviceAlertCountByDayHour 按日每小时设备告警数统计 + GetDeviceAlertCountByDayHour(ctx context.Context, day string) (res []model.CountData, err error) + // GetAlarmTotalCount 告警总数统计(当年、当月、当日),dataType :day,month,year ,date:2021 or 01 or21 + GetAlarmTotalCount(ctx context.Context, dataType, date string) (number int64, err error) + // GetAlarmLevelCount 告警级别统计 + GetAlarmLevelCount(ctx context.Context, dataType, date string) (res []model.CountData, err error) + } + IAnalysisDevice interface { + // GetDeviceDataTotalCount 获取设备消息总数统计,dataType :day,month,year + GetDeviceDataTotalCount(ctx context.Context, dataType string) (number int64, err error) + // GetDeviceOnlineOfflineCount 获取设备在线离线统计 + GetDeviceOnlineOfflineCount(ctx context.Context) (res model.DeviceOnlineOfflineCount, err error) + // GetDeviceDataCountList 按年度每月设备消息统计,dataType 为统计数据类型 year:按年度,统计每个月的,month:按月份,统计每天的。当前的年与月 + GetDeviceDataCountList(ctx context.Context, dateType string) (res []model.CountData, err error) + } + IAnalysisDeviceData interface { + // GetDeviceData 获取设备数据 + GetDeviceData(ctx context.Context, reqData model.DeviceDataReq) (res []interface{}, err error) + // GetDeviceDataForProductByLatest 获取产品下的所有设备最新一条数据 + GetDeviceDataForProductByLatest(ctx context.Context, productKey string) (res []model.DeviceDataRes, err error) + // GetDeviceHistoryData 获取设备历史数据(来自TSD的数据) + GetDeviceHistoryData(ctx context.Context, reqData model.DeviceDataReq) (res []interface{}, err error) + // GetDeviceAlarmLogData 获取设备告警数据 + GetDeviceAlarmLogData(ctx context.Context, reqData *general.SelectReq) (res interface{}, err error) + } +) + +var ( + localAnalysisDevice IAnalysisDevice + localAnalysisDeviceData IAnalysisDeviceData + localAnalysisDeviceDataTsd IAnalysisDeviceDataTsd + localAnalysisProduct IAnalysisProduct + localAnalysisAlarm IAnalysisAlarm +) + +func AnalysisDevice() IAnalysisDevice { + if localAnalysisDevice == nil { + panic("implement not found for interface IAnalysisDevice, forgot register?") + } + return localAnalysisDevice +} + +func RegisterAnalysisDevice(i IAnalysisDevice) { + localAnalysisDevice = i +} + +func AnalysisDeviceData() IAnalysisDeviceData { + if localAnalysisDeviceData == nil { + panic("implement not found for interface IAnalysisDeviceData, forgot register?") + } + return localAnalysisDeviceData +} + +func RegisterAnalysisDeviceData(i IAnalysisDeviceData) { + localAnalysisDeviceData = i +} + +func AnalysisDeviceDataTsd() IAnalysisDeviceDataTsd { + if localAnalysisDeviceDataTsd == nil { + panic("implement not found for interface IAnalysisDeviceDataTsd, forgot register?") + } + return localAnalysisDeviceDataTsd +} + +func RegisterAnalysisDeviceDataTsd(i IAnalysisDeviceDataTsd) { + localAnalysisDeviceDataTsd = i +} + +func AnalysisProduct() IAnalysisProduct { + if localAnalysisProduct == nil { + panic("implement not found for interface IAnalysisProduct, forgot register?") + } + return localAnalysisProduct +} + +func RegisterAnalysisProduct(i IAnalysisProduct) { + localAnalysisProduct = i +} + +func AnalysisAlarm() IAnalysisAlarm { + if localAnalysisAlarm == nil { + panic("implement not found for interface IAnalysisAlarm, forgot register?") + } + return localAnalysisAlarm +} + +func RegisterAnalysisAlarm(i IAnalysisAlarm) { + localAnalysisAlarm = i +} diff --git a/internal/service/common.go b/internal/service/common.go index 933bc2f..9e408dd 100644 --- a/internal/service/common.go +++ b/internal/service/common.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,127 +7,171 @@ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/api/v1/common" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" + "database/sql" + "sagooiot/api/v1/common" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" ) type ( - IBaseDbLink interface { - GetList(ctx context.Context, input *model.BaseDbLinkDoInput) (total int, out []*model.BaseDbLinkOut, err error) - Add(ctx context.Context, input *model.AddBaseDbLinkInput) (err error) - Detail(ctx context.Context, baseDbLinkId int) (entity *entity.BaseDbLink, err error) - Edit(ctx context.Context, input *model.EditBaseDbLinkInput) (err error) - Del(ctx context.Context, BaseDbLinkId int) (err error) - } - ICityData interface { - GetList(ctx context.Context, status int, name string, code string) (data []*entity.CityData, err error) - Add(ctx context.Context, city *entity.CityData) (err error) - Edit(ctx context.Context, city *entity.CityData) (err error) - GetInfoById(ctx context.Context, id int) (cityInfo *entity.CityData, err error) - DelById(ctx context.Context, id int) (err error) - GetAll(ctx context.Context) (data []*entity.CityData, err error) + IUpload interface { + // UploadFiles 上传多文件 + UploadFiles(ctx context.Context, files []*ghttp.UploadFile, checkFileType string, source int) (result common.UploadMultipleRes, err error) + // UploadFile 上传单文件 + UploadFile(ctx context.Context, file *ghttp.UploadFile, checkFileType string, source int) (result common.UploadResponse, err error) + // UploadTencent 上传至腾讯云 + UploadTencent(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) + // UploadLocal 上传本地 + UploadLocal(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) + // CheckSize 检查上传文件大小 + CheckSize(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) + // CheckType 检查上传文件类型 + CheckType(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) + // UploadMinIO 上传至MinIO + UploadMinIO(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) + } + ICheckAuth interface { + // IsToken 验证TOKEN是否正确 + IsToken(ctx context.Context) (isToken bool, expiresAt int64, isAuth string, err error) + // CheckAccessAuth 验证访问权限 + CheckAccessAuth(ctx context.Context, address string) (isAllow bool, err error) } IConfigData interface { + // List 系统参数列表 List(ctx context.Context, input *model.ConfigDoInput) (total int, out []*model.SysConfigOut, err error) Add(ctx context.Context, input *model.AddConfigInput, userId int) (err error) + // CheckConfigKeyUnique 验证参数键名是否存在 CheckConfigKeyUnique(ctx context.Context, configKey string, configId ...int) (err error) + // Get 获取系统参数 Get(ctx context.Context, id int) (out *model.SysConfigOut, err error) + // Edit 修改系统参数 Edit(ctx context.Context, input *model.EditConfigInput, userId int) (err error) + // Delete 删除系统参数 //TODO 转为KEY处理 Delete(ctx context.Context, ids []int) (err error) + // GetConfigByKey 通过key获取参数(从缓存获取) GetConfigByKey(ctx context.Context, key string) (config *entity.SysConfig, err error) + // GetConfigByKeys 通过key数组获取参数(从缓存获取) + GetConfigByKeys(ctx context.Context, keys []string) (out []*entity.SysConfig, err error) + // GetByKey 通过key获取参数(从数据库获取) GetByKey(ctx context.Context, key string) (config *entity.SysConfig, err error) + // GetByKeys 通过keys获取参数(从数据库获取) + GetByKeys(ctx context.Context, keys []string) (config []*entity.SysConfig, err error) + GetSysConfigSetting(ctx context.Context, types int) (out []*entity.SysConfig, err error) + // EditSysConfigSetting 修改系统配置设置 + EditSysConfigSetting(ctx context.Context, inputs []*model.EditConfigInput) (err error) + // GetLoadCache 获取本地缓存配置 + GetLoadCache(ctx context.Context) (conf *model.CacheConfig, err error) } IDictData interface { + // GetDictWithDataByType 通过字典键类型获取选项 GetDictWithDataByType(ctx context.Context, input *model.GetDictInput) (dict *model.GetDictOut, err error) + // List 获取字典数据 List(ctx context.Context, input *model.SysDictSearchInput) (total int, out []*model.SysDictDataOut, err error) Add(ctx context.Context, input *model.AddDictDataInput, userId int) (err error) + // Get 获取字典数据 Get(ctx context.Context, dictCode uint) (out *model.SysDictDataOut, err error) + // Edit 修改字典数据 Edit(ctx context.Context, input *model.EditDictDataInput, userId int) (err error) + // Delete 删除字典数据 Delete(ctx context.Context, ids []int) (err error) + // GetDictDataByType 通过字典键类型获取选项 + GetDictDataByType(ctx context.Context, dictType string) (dict *model.GetDictOut, err error) } IDictType interface { + // List 字典类型列表 List(ctx context.Context, input *model.DictTypeDoInput) (total int, out []*model.SysDictTypeInfoOut, err error) + // Add 添加字典类型 Add(ctx context.Context, input *model.AddDictTypeInput, userId int) (err error) + // Edit 修改字典类型 Edit(ctx context.Context, input *model.EditDictTypeInput, userId int) (err error) Get(ctx context.Context, req *common.DictTypeGetReq) (dictType *model.SysDictTypeOut, err error) + // ExistsDictType 检查类型是否已经存在 ExistsDictType(ctx context.Context, dictType string, dictId ...int) (err error) + // Delete 删除字典类型 Delete(ctx context.Context, dictIds []int) (err error) } - IUpload interface { - UploadFiles(ctx context.Context, files []*ghttp.UploadFile, checkFileType string, source int) (result common.UploadMultipleRes, err error) - UploadFile(ctx context.Context, file *ghttp.UploadFile, checkFileType string, source int) (result common.UploadResponse, err error) - UploadTencent(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) - UploadLocal(ctx context.Context, file *ghttp.UploadFile) (result common.UploadResponse, err error) - CheckSize(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) - CheckType(ctx context.Context, checkFileType string, file *ghttp.UploadFile) (err error) + IPgSequences interface { + // GetPgSequences 获取PG指定表序列信息 + GetPgSequences(ctx context.Context, tableName string, primaryKey string) (out *model.PgSequenceOut, err error) + } + ISequences interface { + // GetSequences 获取主键ID + GetSequences(ctx context.Context, result sql.Result, tableName string, primaryKey string) (lastInsertId int64, err error) + } + ISysInfo interface { + GetSysInfo(ctx context.Context) (out g.Map, err error) + // ServerInfoEscalation 客户端服务信息上报 + ServerInfoEscalation(ctx context.Context) (err error) } ) var ( - localUpload IUpload - localBaseDbLink IBaseDbLink - localCityData ICityData - localConfigData IConfigData - localDictData IDictData - localDictType IDictType + localSysInfo ISysInfo + localUpload IUpload + localCheckAuth ICheckAuth + localConfigData IConfigData + localDictData IDictData + localDictType IDictType + localPgSequences IPgSequences + localSequences ISequences ) -func BaseDbLink() IBaseDbLink { - if localBaseDbLink == nil { - panic("implement not found for interface IBaseDbLink, forgot register?") +func DictData() IDictData { + if localDictData == nil { + panic("implement not found for interface IDictData, forgot register?") } - return localBaseDbLink + return localDictData } -func RegisterBaseDbLink(i IBaseDbLink) { - localBaseDbLink = i +func RegisterDictData(i IDictData) { + localDictData = i } -func CityData() ICityData { - if localCityData == nil { - panic("implement not found for interface ICityData, forgot register?") +func DictType() IDictType { + if localDictType == nil { + panic("implement not found for interface IDictType, forgot register?") } - return localCityData + return localDictType } -func RegisterCityData(i ICityData) { - localCityData = i +func RegisterDictType(i IDictType) { + localDictType = i } -func ConfigData() IConfigData { - if localConfigData == nil { - panic("implement not found for interface IConfigData, forgot register?") +func PgSequences() IPgSequences { + if localPgSequences == nil { + panic("implement not found for interface IPgSequences, forgot register?") } - return localConfigData + return localPgSequences } -func RegisterConfigData(i IConfigData) { - localConfigData = i +func RegisterPgSequences(i IPgSequences) { + localPgSequences = i } -func DictData() IDictData { - if localDictData == nil { - panic("implement not found for interface IDictData, forgot register?") +func Sequences() ISequences { + if localSequences == nil { + panic("implement not found for interface ISequences, forgot register?") } - return localDictData + return localSequences } -func RegisterDictData(i IDictData) { - localDictData = i +func RegisterSequences(i ISequences) { + localSequences = i } -func DictType() IDictType { - if localDictType == nil { - panic("implement not found for interface IDictType, forgot register?") +func SysInfo() ISysInfo { + if localSysInfo == nil { + panic("implement not found for interface ISysInfo, forgot register?") } - return localDictType + return localSysInfo } -func RegisterDictType(i IDictType) { - localDictType = i +func RegisterSysInfo(i ISysInfo) { + localSysInfo = i } func Upload() IUpload { @@ -140,3 +184,25 @@ func Upload() IUpload { func RegisterUpload(i IUpload) { localUpload = i } + +func CheckAuth() ICheckAuth { + if localCheckAuth == nil { + panic("implement not found for interface ICheckAuth, forgot register?") + } + return localCheckAuth +} + +func RegisterCheckAuth(i ICheckAuth) { + localCheckAuth = i +} + +func ConfigData() IConfigData { + if localConfigData == nil { + panic("implement not found for interface IConfigData, forgot register?") + } + return localConfigData +} + +func RegisterConfigData(i IConfigData) { + localConfigData = i +} diff --git a/internal/service/context.go b/internal/service/context.go index ecd9fd2..14ccdb5 100644 --- a/internal/service/context.go +++ b/internal/service/context.go @@ -1,26 +1,39 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" "github.com/gogf/gf/v2/net/ghttp" ) -type IContext interface { - Init(r *ghttp.Request, customCtx *model.Context) - Get(ctx context.Context) *model.Context - SetUser(ctx context.Context, ctxUser *model.ContextUser) - GetLoginUser(ctx context.Context) *model.ContextUser - GetUserId(ctx context.Context) int - GetUserDeptId(ctx context.Context) int - GetChildrenDeptId(ctx context.Context) []int - GetRequestWay(ctx context.Context) string -} +type ( + IContext interface { + // Init 初始化上下文对象指针到上下文对象中,以便后续的请求流程中可以修改。 + Init(r *ghttp.Request, customCtx *model.Context) + // Get 获得上下文变量,如果没有设置,那么返回nil + Get(ctx context.Context) *model.Context + // SetUser 将上下文信息设置到上下文请求中,注意是完整覆盖 + SetUser(ctx context.Context, ctxUser *model.ContextUser) + // GetLoginUser 获取当前登陆用户信息 + GetLoginUser(ctx context.Context) *model.ContextUser + // GetUserId 获取当前登录用户id + GetUserId(ctx context.Context) int + // GetUserDeptId 获取当前登录用户部门ID + GetUserDeptId(ctx context.Context) int + // GetChildrenDeptId 获取所有子部门ID + GetChildrenDeptId(ctx context.Context) []int + // GetUserName 获取当前登录用户账户 + GetUserName(ctx context.Context) string + // GetRequestWay 获取当前系统请求方式 + GetRequestWay(ctx context.Context) string + } +) var ( localContext IContext diff --git a/internal/service/datahub.go b/internal/service/datahub.go deleted file mode 100644 index c94d3b5..0000000 --- a/internal/service/datahub.go +++ /dev/null @@ -1,179 +0,0 @@ -// ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// You can delete these comments if you wish manually maintain this interface file. -// ================================================================================ - -package service - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" - - "github.com/go-gota/gota/dataframe" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -type ( - IDataSource interface { - Add(ctx context.Context, in *model.DataSourceApiAddInput) (sourceId uint64, err error) - Edit(ctx context.Context, in *model.DataSourceApiEditInput) (err error) - Del(ctx context.Context, ids []uint64) (err error) - Search(ctx context.Context, in *model.DataSourceSearchInput) (out *model.DataSourceSearchOutput, err error) - List(ctx context.Context) (list []*entity.DataSource, err error) - Detail(ctx context.Context, sourceId uint64) (out *model.DataSourceOutput, err error) - Deploy(ctx context.Context, sourceId uint64) (err error) - Undeploy(ctx context.Context, sourceId uint64) (err error) - UpdateData(ctx context.Context, sourceId uint64) (err error) - GetData(ctx context.Context, in *model.DataSourceDataInput) (out *model.DataSourceDataOutput, err error) - GetAllData(ctx context.Context, in *model.SourceDataAllInput) (out *model.SourceDataAllOutput, err error) - AllSource(ctx context.Context) (out []*model.AllSourceOut, err error) - CopeSource(ctx context.Context, sourceId uint64) (err error) - UpdateInterval(ctx context.Context, sourceId uint64, cronExpression string) (err error) - GetApiData(ctx context.Context, sourceId uint64) (apiData []string, err error) - AddDb(ctx context.Context, in *model.DataSourceDbAddInput) (sourceId uint64, err error) - EditDb(ctx context.Context, in *model.DataSourceDbEditInput) (err error) - GetDbFields(ctx context.Context, sourceId uint64) (g.MapStrAny, error) - GetDbData(ctx context.Context, sourceId uint64) (string, error) - AddDevice(ctx context.Context, in *model.DataSourceDeviceAddInput) (sourceId uint64, err error) - EditDevice(ctx context.Context, in *model.DataSourceDeviceEditInput) (err error) - GetDeviceData(ctx context.Context, sourceId uint64) (string, error) - } - IDataSourceRecord interface { - GetForTpl(ctx context.Context, sourceId uint64, tid uint64) (rs gdb.Result, err error) - } - IDataTemplate interface { - Add(ctx context.Context, in *model.DataTemplateAddInput) (id uint64, err error) - Edit(ctx context.Context, in *model.DataTemplateEditInput) (err error) - Del(ctx context.Context, ids []uint64) (err error) - Search(ctx context.Context, in *model.DataTemplateSearchInput) (out *model.DataTemplateSearchOutput, err error) - List(ctx context.Context) (list []*entity.DataTemplate, err error) - Detail(ctx context.Context, id uint64) (out *model.DataTemplateOutput, err error) - Deploy(ctx context.Context, id uint64) (err error) - Undeploy(ctx context.Context, id uint64) (err error) - GetData(ctx context.Context, in *model.DataTemplateDataInput) (out *model.DataTemplateDataOutput, err error) - GetAllData(ctx context.Context, in *model.TemplateDataAllInput) (out *model.TemplateDataAllOutput, err error) - GetDataBySql(ctx context.Context, sql string) (df dataframe.DataFrame, err error) - GetDataByTableName(ctx context.Context, tableName string) (df dataframe.DataFrame, err error) - GetLastData(ctx context.Context, in *model.TemplateDataLastInput) (out *model.TemplateDataLastOutput, err error) - UpdateData(ctx context.Context, id uint64) error - GetInfoByIds(ctx context.Context, ids []uint64) (data []*entity.DataTemplate, err error) - AllTemplate(ctx context.Context) (out []*model.AllTemplateOut, err error) - UpdateInterval(ctx context.Context, id uint64, cronExpression string) (err error) - CopeTemplate(ctx context.Context, id uint64) (err error) - CheckRelation(ctx context.Context, id uint64) (yes bool, err error) - SetRelation(ctx context.Context, in *model.TemplateDataRelationInput) (err error) - SourceList(ctx context.Context, id uint64) (list []*model.DataSourceOutput, err error) - } - IDataTemplateBusi interface { - Add(ctx context.Context, in *model.DataTemplateBusiAddInput) (err error) - GetInfos(ctx context.Context, busiTypes int) (data *entity.DataTemplateBusi, err error) - GetInfo(ctx context.Context, busiTypes int) (data *entity.DataTemplateBusi, err error) - GetTable(ctx context.Context, busiTypes int) (table string, err error) - Del(ctx context.Context, tid uint64) error - } - IDataTemplateNode interface { - Add(ctx context.Context, in *model.DataTemplateNodeAddInput) (err error) - Edit(ctx context.Context, in *model.DataTemplateNodeEditInput) (err error) - Del(ctx context.Context, id uint64) (err error) - List(ctx context.Context, tid uint64) (list []*model.DataTemplateNodeOutput, err error) - } - IDataTemplateRecord interface { - UpdateData(ctx context.Context, tid uint64) error - } - IDataNode interface { - Add(ctx context.Context, in *model.DataNodeAddInput) (err error) - Edit(ctx context.Context, in *model.DataNodeEditInput) (err error) - Del(ctx context.Context, nodeId uint64) (err error) - List(ctx context.Context, sourceId uint64) (list []*model.DataNodeOutput, err error) - Detail(ctx context.Context, nodeId uint64) (out *model.DataNodeOutput, err error) - } -) - -var ( - localDataNode IDataNode - localDataSource IDataSource - localDataSourceRecord IDataSourceRecord - localDataTemplate IDataTemplate - localDataTemplateBusi IDataTemplateBusi - localDataTemplateNode IDataTemplateNode - localDataTemplateRecord IDataTemplateRecord -) - -func DataTemplateBusi() IDataTemplateBusi { - if localDataTemplateBusi == nil { - panic("implement not found for interface IDataTemplateBusi, forgot register?") - } - return localDataTemplateBusi -} - -func RegisterDataTemplateBusi(i IDataTemplateBusi) { - localDataTemplateBusi = i -} - -func DataTemplateNode() IDataTemplateNode { - if localDataTemplateNode == nil { - panic("implement not found for interface IDataTemplateNode, forgot register?") - } - return localDataTemplateNode -} - -func RegisterDataTemplateNode(i IDataTemplateNode) { - localDataTemplateNode = i -} - -func DataTemplateRecord() IDataTemplateRecord { - if localDataTemplateRecord == nil { - panic("implement not found for interface IDataTemplateRecord, forgot register?") - } - return localDataTemplateRecord -} - -func RegisterDataTemplateRecord(i IDataTemplateRecord) { - localDataTemplateRecord = i -} - -func DataNode() IDataNode { - if localDataNode == nil { - panic("implement not found for interface IDataNode, forgot register?") - } - return localDataNode -} - -func RegisterDataNode(i IDataNode) { - localDataNode = i -} - -func DataSource() IDataSource { - if localDataSource == nil { - panic("implement not found for interface IDataSource, forgot register?") - } - return localDataSource -} - -func RegisterDataSource(i IDataSource) { - localDataSource = i -} - -func DataSourceRecord() IDataSourceRecord { - if localDataSourceRecord == nil { - panic("implement not found for interface IDataSourceRecord, forgot register?") - } - return localDataSourceRecord -} - -func RegisterDataSourceRecord(i IDataSourceRecord) { - localDataSourceRecord = i -} - -func DataTemplate() IDataTemplate { - if localDataTemplate == nil { - panic("implement not found for interface IDataTemplate, forgot register?") - } - return localDataTemplate -} - -func RegisterDataTemplate(i IDataTemplate) { - localDataTemplate = i -} diff --git a/internal/service/envirotronics.go b/internal/service/envirotronics.go deleted file mode 100644 index bc61253..0000000 --- a/internal/service/envirotronics.go +++ /dev/null @@ -1,35 +0,0 @@ -// ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// You can delete these comments if you wish manually maintain this interface file. -// ================================================================================ - -package service - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/model" -) - -type ( - IEnvWeather interface { - CityWeatherList(ctx context.Context) (cityWeatherListOut []*model.CityWeatherListOut, err error) - GetCityWeatherById(ctx context.Context, id int) (cityWeatherListOut *model.CityWeatherListOut, err error) - GetCityTemperatureById(ctx context.Context, id int, types int) (cityWeatherEchartOut []*model.CityWeatherEchartOut, avgCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastAvgCityWeatherEchartOut []*model.CityWeatherEchartOut, err error) - GetCityWindpowerById(ctx context.Context, id int, types int) (cityWeatherEchartOut []*model.CityWeatherEchartOut, avgCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastCityWeatherEchartOut []*model.CityWeatherEchartOut, foreCastAvgCityWeatherEchartOut []*model.CityWeatherEchartOut, err error) - } -) - -var ( - localEnvWeather IEnvWeather -) - -func EnvWeather() IEnvWeather { - if localEnvWeather == nil { - panic("implement not found for interface IEnvWeather, forgot register?") - } - return localEnvWeather -} - -func RegisterEnvWeather(i IEnvWeather) { - localEnvWeather = i -} diff --git a/internal/service/middleware.go b/internal/service/middleware.go index 6e82492..eadc501 100644 --- a/internal/service/middleware.go +++ b/internal/service/middleware.go @@ -1,6 +1,7 @@ -// ========================================================================== -// Code generated by GoFrame CLI tool. DO NOT EDIT. -// ========================================================================== +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ package service @@ -8,15 +9,26 @@ import ( "github.com/gogf/gf/v2/net/ghttp" ) -type IMiddleware interface { - ResponseHandler(r *ghttp.Request) - Ctx(r *ghttp.Request) - Auth(r *ghttp.Request) - MiddlewareCORS(r *ghttp.Request) - OperationLog(r *ghttp.Request) -} +type ( + IMiddleware interface { + // ResponseHandler 返回处理中间件 + ResponseHandler(r *ghttp.Request) + // Ctx 自定义上下文对象 + Ctx(r *ghttp.Request) + // Auth 前台系统权限控制,用户必须登录才能访问 + Auth(r *ghttp.Request) + // MiddlewareCORS 跨域处理 + MiddlewareCORS(r *ghttp.Request) + // OperationLog 操作日志 + OperationLog(r *ghttp.Request) + Tracing(r *ghttp.Request) + I18n(r *ghttp.Request) + } +) -var localMiddleware IMiddleware +var ( + localMiddleware IMiddleware +) func Middleware() IMiddleware { if localMiddleware == nil { diff --git a/internal/service/network.go b/internal/service/network.go index 4e4384d..d60a41b 100644 --- a/internal/service/network.go +++ b/internal/service/network.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,26 +7,42 @@ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" ) type ( INetworkServer interface { + // GetServerList 获取列表数据 GetServerList(ctx context.Context, in *model.GetNetworkServerListInput) (total int, out []*model.NetworkServerOut, err error) + // GetServerRunList 获取可运行的服务列表数据 GetServerRunList(ctx context.Context) (list []*model.NetworkServerRes, err error) + // GetServerById 获取指定ID数据 GetServerById(ctx context.Context, id int) (out *model.NetworkServerOut, err error) + // AddServer 添加数据 todo 需要处理 AddServer(ctx context.Context, in model.NetworkServerAddInput) (err error) + // EditServer 修改数据 todo 需要处理 EditServer(ctx context.Context, in model.NetworkServerEditInput) (err error) + // 删除数据 + // todo 需要处理 DeleteServer(ctx context.Context, ids []int) (err error) + // SetServerStatus 修改状态数据 todo 需要处理 SetServerStatus(ctx context.Context, id, status int) (err error) } INetworkTunnel interface { + // GetTunnelList 获取列表数据 GetTunnelList(ctx context.Context, in *model.GetNetworkTunnelListInput) (total int, out []*model.NetworkTunnelOut, err error) + // GetTunnelRunList 获取列表数据 GetTunnelRunList(ctx context.Context) (out []*model.NetworkTunnelOut, err error) + // 获取指定ID数据 GetTunnelById(ctx context.Context, id int) (out *model.NetworkTunnelOut, err error) + // TODO 这里更改了请求参数,需要确认是否ok + // AddTunnel 添加数据 AddTunnel(ctx context.Context, in model.NetworkTunnelAddInput) (id int, err error) + // EditTunnel 修改数据 EditTunnel(ctx context.Context, in model.NetworkTunnelEditInput) (err error) + // DeleteTunnel 删除数据 DeleteTunnel(ctx context.Context, ids []int) (err error) + // SetTunnelStatus 修改状态数据 SetTunnelStatus(ctx context.Context, id, status int) (err error) } ) diff --git a/internal/service/notice.go b/internal/service/notice.go index be75d32..284d8df 100644 --- a/internal/service/notice.go +++ b/internal/service/notice.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,59 +7,72 @@ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" ) type ( + INoticeConfig interface { + // GetNoticeConfigList 获取列表数据 + GetNoticeConfigList(ctx context.Context, in *model.GetNoticeConfigListInput) (total, page int, list []*model.NoticeConfigOutput, err error) + // GetNoticeConfigById 获取指定ID数据 + GetNoticeConfigById(ctx context.Context, id string) (out *model.NoticeConfigOutput, err error) + // AddNoticeConfig 添加数据 + AddNoticeConfig(ctx context.Context, in model.NoticeConfigAddInput) (err error) + // EditNoticeConfig 修改数据 + EditNoticeConfig(ctx context.Context, in model.NoticeConfigEditInput) (err error) + // DeleteNoticeConfig 删除数据 + DeleteNoticeConfig(ctx context.Context, Ids []string) (err error) + } + INoticeInfo interface { + // GetNoticeInfoList 获取列表数据 + GetNoticeInfoList(ctx context.Context, in *model.GetNoticeInfoListInput) (total, page int, list []*model.NoticeInfoOutput, err error) + // GetNoticeInfoById 获取指定ID数据 + GetNoticeInfoById(ctx context.Context, id int) (out *model.NoticeInfoOutput, err error) + // AddNoticeInfo 添加数据 + AddNoticeInfo(ctx context.Context, in model.NoticeInfoAddInput) (err error) + // EditNoticeInfo 修改数据 + EditNoticeInfo(ctx context.Context, in model.NoticeInfoEditInput) (err error) + // DeleteNoticeInfo 删除数据 + DeleteNoticeInfo(ctx context.Context, Ids []int) (err error) + } INoticeLog interface { + // Add 通知日志记录 Add(ctx context.Context, in *model.NoticeLogAddInput) (err error) + // Del 删除日志 Del(ctx context.Context, ids []uint64) (err error) + // GetInfoById 获取日志信息 + GetInfoById(ctx context.Context, id uint64) (out *entity.NoticeLog, err error) + // Search 搜索 Search(ctx context.Context, in *model.NoticeLogSearchInput) (out *model.NoticeLogSearchOutput, err error) + // ClearLogByDays 按日期删除日志 ClearLogByDays(ctx context.Context, days int) (err error) } INoticeTemplate interface { + // GetNoticeTemplateList 获取列表数据 GetNoticeTemplateList(ctx context.Context, in *model.GetNoticeTemplateListInput) (total, page int, list []*model.NoticeTemplateOutput, err error) + // GetNoticeTemplateById 获取指定ID数据 GetNoticeTemplateById(ctx context.Context, id string) (out *model.NoticeTemplateOutput, err error) + // GetNoticeTemplateByConfigId 获取指定ConfigID数据 GetNoticeTemplateByConfigId(ctx context.Context, configId string) (out *model.NoticeTemplateOutput, err error) + // AddNoticeTemplate 添加数据 AddNoticeTemplate(ctx context.Context, in model.NoticeTemplateAddInput) (err error) + // EditNoticeTemplate 修改数据 EditNoticeTemplate(ctx context.Context, in model.NoticeTemplateEditInput) (err error) + // SaveNoticeTemplate 直接更新数据 SaveNoticeTemplate(ctx context.Context, in model.NoticeTemplateAddInput) (err error) + // DeleteNoticeTemplate 删除数据 DeleteNoticeTemplate(ctx context.Context, Ids []string) (err error) } - INoticeConfig interface { - GetNoticeConfigList(ctx context.Context, in *model.GetNoticeConfigListInput) (total, page int, list []*model.NoticeConfigOutput, err error) - GetNoticeConfigById(ctx context.Context, id int) (out *model.NoticeConfigOutput, err error) - AddNoticeConfig(ctx context.Context, in model.NoticeConfigAddInput) (err error) - EditNoticeConfig(ctx context.Context, in model.NoticeConfigEditInput) (err error) - DeleteNoticeConfig(ctx context.Context, Ids []string) (err error) - } - INoticeInfo interface { - GetNoticeInfoList(ctx context.Context, in *model.GetNoticeInfoListInput) (total, page int, list []*model.NoticeInfoOutput, err error) - GetNoticeInfoById(ctx context.Context, id int) (out *model.NoticeInfoOutput, err error) - AddNoticeInfo(ctx context.Context, in model.NoticeInfoAddInput) (err error) - EditNoticeInfo(ctx context.Context, in model.NoticeInfoEditInput) (err error) - DeleteNoticeInfo(ctx context.Context, Ids []int) (err error) - } ) var ( - localNoticeConfig INoticeConfig localNoticeInfo INoticeInfo localNoticeLog INoticeLog localNoticeTemplate INoticeTemplate + localNoticeConfig INoticeConfig ) -func NoticeTemplate() INoticeTemplate { - if localNoticeTemplate == nil { - panic("implement not found for interface INoticeTemplate, forgot register?") - } - return localNoticeTemplate -} - -func RegisterNoticeTemplate(i INoticeTemplate) { - localNoticeTemplate = i -} - func NoticeConfig() INoticeConfig { if localNoticeConfig == nil { panic("implement not found for interface INoticeConfig, forgot register?") @@ -92,3 +105,14 @@ func NoticeLog() INoticeLog { func RegisterNoticeLog(i INoticeLog) { localNoticeLog = i } + +func NoticeTemplate() INoticeTemplate { + if localNoticeTemplate == nil { + panic("implement not found for interface INoticeTemplate, forgot register?") + } + return localNoticeTemplate +} + +func RegisterNoticeTemplate(i INoticeTemplate) { + localNoticeTemplate = i +} diff --git a/internal/service/product.go b/internal/service/product.go index 4fd6af9..dcf57e9 100644 --- a/internal/service/product.go +++ b/internal/service/product.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,59 +7,65 @@ package service import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/api/v1/product" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "time" + + "github.com/gogf/gf/v2/net/ghttp" ) type ( - IDevTSLTag interface { - ListTag(ctx context.Context, in *model.ListTSLTagInput) (out *model.ListTSLTagOutput, err error) - AddTag(ctx context.Context, in *model.TSLTagInput) (err error) - EditTag(ctx context.Context, in *model.TSLTagInput) (err error) - DelTag(ctx context.Context, in *model.DelTSLTagInput) (err error) - } - IDevDevice interface { - Get(ctx context.Context, key string) (out *model.DeviceOutput, err error) - Detail(ctx context.Context, id uint) (out *model.DeviceOutput, err error) - ListForPage(ctx context.Context, in *model.ListDeviceForPageInput) (out *model.ListDeviceForPageOutput, err error) - List(ctx context.Context, in *model.ListDeviceInput) (list []*model.DeviceOutput, err error) - Add(ctx context.Context, in *model.AddDeviceInput) (deviceId uint, err error) - Edit(ctx context.Context, in *model.EditDeviceInput) (err error) - Del(ctx context.Context, ids []uint) (err error) - Deploy(ctx context.Context, id uint) (err error) - Undeploy(ctx context.Context, id uint) (err error) - Online(ctx context.Context, key string) (err error) - Offline(ctx context.Context, key string) (err error) - TotalByProductId(ctx context.Context, productIds []uint) (totals map[uint]int, err error) - Total(ctx context.Context) (data model.DeviceTotalOutput, err error) - TotalForMonths(ctx context.Context) (data map[int]int, err error) - AlarmTotalForMonths(ctx context.Context) (data map[int]int, err error) - RunStatus(ctx context.Context, id uint) (out *model.DeviceRunStatusOutput, err error) - GetProperty(ctx context.Context, in *model.DeviceGetPropertyInput) (out *model.DevicePropertiy, err error) - GetPropertyList(ctx context.Context, in *model.DeviceGetPropertyListInput) (out *model.DeviceGetPropertyListOutput, err error) - } - IDevDeviceLog interface { - LogType(ctx context.Context) (list []string) - Search(ctx context.Context, in *model.DeviceLogSearchInput) (out *model.DeviceLogSearchOutput, err error) - } - IDevDeviceTag interface { - Add(ctx context.Context, in *model.AddTagDeviceInput) (err error) - Edit(ctx context.Context, in *model.EditTagDeviceInput) (err error) - Del(ctx context.Context, id uint) (err error) + IDevDeviceProperty interface { + // Set 设备属性设置 + Set(ctx context.Context, in *model.DevicePropertyInput) (out *model.DevicePropertyOutput, err error) } IDevProduct interface { - Get(ctx context.Context, key string) (out *model.DetailProductOutput, err error) - Detail(ctx context.Context, id uint) (out *model.DetailProductOutput, err error) + Detail(ctx context.Context, key string) (out *model.DetailProductOutput, err error) + GetInfoById(ctx context.Context, id uint) (out *entity.DevProduct, err error) GetNameByIds(ctx context.Context, productIds []uint) (names map[uint]string, err error) ListForPage(ctx context.Context, in *model.ListForPageInput) (out *model.ListForPageOutput, err error) List(ctx context.Context) (list []*model.ProductOutput, err error) Add(ctx context.Context, in *model.AddProductInput) (err error) Edit(ctx context.Context, in *model.EditProductInput) (err error) - Del(ctx context.Context, ids []uint) (err error) - Deploy(ctx context.Context, id uint) (err error) - Undeploy(ctx context.Context, id uint) (err error) + UpdateExtend(ctx context.Context, in *model.ExtendInput) (err error) + Del(ctx context.Context, keys []string) (err error) + // Deploy 产品发布 + Deploy(ctx context.Context, productKey string) (err error) + // Undeploy 产品停用 + Undeploy(ctx context.Context, productKey string) (err error) + // ListForSub 子设备类型产品 + ListForSub(ctx context.Context) (list []*model.ProductOutput, err error) + // UpdateScriptInfo 脚本更新 + UpdateScriptInfo(ctx context.Context, in *model.ScriptInfoInput) (err error) + // ConnectIntro 获取设备接入信息 + ConnectIntro(ctx context.Context, productKey string) (out *model.DeviceConnectIntroOutput, err error) } - IDevTSLDataType interface { - DataTypeValueList(ctx context.Context) (out *model.DataTypeOutput, err error) + IDevTSLEvent interface { + Detail(ctx context.Context, deviceKey string, eventKey string) (event *model.TSLEvent, err error) + ListEvent(ctx context.Context, in *model.ListTSLEventInput) (out *model.ListTSLEventOutput, err error) + AllEvent(ctx context.Context, key string) (list []model.TSLEvent, err error) + AddEvent(ctx context.Context, in *model.TSLEventAddInput) (err error) + EditEvent(ctx context.Context, in *model.TSLEventAddInput) (err error) + DelEvent(ctx context.Context, in *model.DelTSLEventInput) (err error) + } + IDevTSLImport interface { + // Export 导出物模型 + Export(ctx context.Context, key string) (err error) + // Import 导入物模型 + Import(ctx context.Context, key string, file *ghttp.UploadFile) (err error) + } + IDevDeviceFunction interface { + // Do 执行设备功能 + Do(ctx context.Context, in *model.DeviceFunctionInput) (out *model.DeviceFunctionOutput, err error) + } + IDevDeviceLog interface { + // LogType 日志类型 + LogType(ctx context.Context) (list []string) + // Search 日志搜索 + Search(ctx context.Context, in *model.DeviceLogSearchInput) (out *model.DeviceLogSearchOutput, err error) } IDevTSLFunction interface { ListFunction(ctx context.Context, in *model.ListTSLFunctionInput) (out *model.ListTSLFunctionOutput, err error) @@ -68,79 +74,186 @@ type ( EditFunction(ctx context.Context, in *model.TSLFunctionAddInput) (err error) DelFunction(ctx context.Context, in *model.DelTSLFunctionInput) (err error) } + IDevTSLProperty interface { + ListProperty(ctx context.Context, in *model.ListTSLPropertyInput) (out *model.ListTSLPropertyOutput, err error) + AllProperty(ctx context.Context, key string) (list []model.TSLProperty, err error) + AddProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) + EditProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) + DelProperty(ctx context.Context, in *model.DelTSLPropertyInput) (err error) + } + IDevDeviceTag interface { + Add(ctx context.Context, in *model.AddTagDeviceInput) (err error) + Edit(ctx context.Context, in *model.EditTagDeviceInput) (err error) + Del(ctx context.Context, id uint) (err error) + Update(ctx context.Context, deviceId uint, list []model.AddTagDeviceInput) (err error) + } + IDevTSLDataType interface { + DataTypeValueList(ctx context.Context) (out *model.DataTypeOutput, err error) + } + IDevTSLTag interface { + ListTag(ctx context.Context, in *model.ListTSLTagInput) (out *model.ListTSLTagOutput, err error) + AddTag(ctx context.Context, in *model.TSLTagInput) (err error) + EditTag(ctx context.Context, in *model.TSLTagInput) (err error) + DelTag(ctx context.Context, in *model.DelTSLTagInput) (err error) + } IDevCategory interface { Detail(ctx context.Context, id uint) (out *model.ProductCategoryOutput, err error) GetNameByIds(ctx context.Context, categoryIds []uint) (names map[uint]string, err error) + // ListForPage 产品分类列表 ListForPage(ctx context.Context, page, limit int, name string) (out []*model.ProductCategoryTreeOutput, total int, err error) List(ctx context.Context, name string) (out []*model.ProductCategoryTreeOutput, err error) Add(ctx context.Context, in *model.AddProductCategoryInput) (err error) Edit(ctx context.Context, in *model.EditProductCategoryInput) (err error) Del(ctx context.Context, id uint) (err error) } - IDevDataReport interface { - Event(ctx context.Context, deviceKey string, data map[string]any) (err error) + IDevTSLParse interface { + // ParseData 基于物模型解析上报数据 + ParseData(ctx context.Context, deviceKey string, data []byte) (res iotModel.ReportPropertyData, err error) + // HandleProperties 处理属性 + HandleProperties(ctx context.Context, device *model.DeviceOutput, properties map[string]interface{}) (reportDataInfo iotModel.ReportPropertyData, err error) + // HandleEvents 处理事件上报 + HandleEvents(ctx context.Context, device *model.DeviceOutput, events map[string]sagooProtocol.EventNode) (res []iotModel.ReportEventData, err error) } - IDevTSLEvent interface { - Detail(ctx context.Context, deviceKey string, eventKey string) (event *model.TSLEvent, err error) - ListEvent(ctx context.Context, in *model.ListTSLEventInput) (out *model.ListTSLEventOutput, err error) - AddEvent(ctx context.Context, in *model.TSLEventInput) (err error) - EditEvent(ctx context.Context, in *model.TSLEventInput) (err error) - DelEvent(ctx context.Context, in *model.DelTSLEventInput) (err error) + IDevDeviceTree interface { + // List 设备树列表 + List(ctx context.Context) (out []*model.DeviceTreeListOutput, err error) + // Change 更换上下级 + Change(ctx context.Context, infoId, parentInfoId int) error + // Detail 信息详情 + Detail(ctx context.Context, infoId int) (out *model.DetailDeviceTreeInfoOutput, err error) + // Add 添加设备树基本信息 + Add(ctx context.Context, in *model.AddDeviceTreeInfoInput) error + // Edit 修改设备树基本信息 + Edit(ctx context.Context, in *model.EditDeviceTreeInfoInput) error + // Del 删除设备树基本信息 + Del(ctx context.Context, infoId int) error } - IDevTSLProperty interface { - ListProperty(ctx context.Context, in *model.ListTSLPropertyInput) (out *model.ListTSLPropertyOutput, err error) - AllProperty(ctx context.Context, key string) (list []model.TSLProperty, err error) - AddProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) - EditProperty(ctx context.Context, in *model.TSLPropertyInput) (err error) - DelProperty(ctx context.Context, in *model.DelTSLPropertyInput) (err error) + IDevInit interface { + // InitProductForTd 产品表结构初始化 + InitProductForTd(ctx context.Context) (err error) + // InitDeviceForTd 设备表结构初始化 + InitDeviceForTd(ctx context.Context) (err error) + } + IDevDataReport interface { + // Event 设备事件上报 + Event(ctx context.Context, deviceKey string, data model.ReportEventData, subKey ...string) error + // Property 设备属性上报 + Property(ctx context.Context, deviceKey string, data model.ReportPropertyData, subKey ...string) error + } + IDevDevice interface { + // Get 获取设备详情 + Get(ctx context.Context, key string) (out *model.DeviceOutput, err error) + // GetAll 获取所有设备 + GetAll(ctx context.Context) (out []*entity.DevDevice, err error) + Detail(ctx context.Context, key string) (out *model.DeviceOutput, err error) + ListForPage(ctx context.Context, in *model.ListDeviceForPageInput) (out *model.ListDeviceForPageOutput, err error) + // List 已发布产品的设备列表 + List(ctx context.Context, productKey string, keyWord string) (list []*model.DeviceOutput, err error) + Add(ctx context.Context, in *model.AddDeviceInput) (deviceId uint, err error) + Edit(ctx context.Context, in *model.EditDeviceInput) (err error) + // UpdateDeviceStatusInfo 更新设备状态信息,设备上线、离线、注册 + UpdateDeviceStatusInfo(ctx context.Context, deviceKey string, status int, timestamp time.Time) (err error) + // BatchUpdateDeviceStatusInfo 批量更新设备状态信息,设备上线、离线、注册 + BatchUpdateDeviceStatusInfo(ctx context.Context, deviceStatusLogList []iotModel.DeviceStatusLog) (err error) + UpdateExtend(ctx context.Context, in *model.DeviceExtendInput) (err error) + Del(ctx context.Context, keys []string) (err error) + // Deploy 设备启用 + Deploy(ctx context.Context, key string) (err error) + // Undeploy 设备禁用 + Undeploy(ctx context.Context, key string) (err error) + // TotalByProductKey 统计产品下的设备数量 + TotalByProductKey(ctx context.Context, productKeys []string) (totals map[string]int, err error) + // RunStatus 运行状态 + RunStatus(ctx context.Context, deviceKey string) (out *model.DeviceRunStatusOutput, err error) + // GetLatestProperty 获取设备最新的属性值 + GetLatestProperty(ctx context.Context, key string) (list []model.DeviceLatestProperty, err error) + // GetProperty 获取指定属性值 + GetProperty(ctx context.Context, in *model.DeviceGetPropertyInput) (out *model.DevicePropertiy, err error) + // GetPropertyList 设备属性详情列表 + GetPropertyList(ctx context.Context, in *model.DeviceGetPropertyListInput) (out *model.DeviceGetPropertyListOutput, err error) + // GetData 获取设备指定日期属性数据 + GetData(ctx context.Context, in *model.DeviceGetDataInput) (list []model.DevicePropertiyOut, err error) + // BindSubDevice 网关绑定子设备 + BindSubDevice(ctx context.Context, in *model.DeviceBindInput) error + // UnBindSubDevice 网关解绑子设备 + UnBindSubDevice(ctx context.Context, in *model.DeviceBindInput) error + // BindList 已绑定列表(分页) + BindList(ctx context.Context, in *model.DeviceBindListInput) (out *model.DeviceBindListOutput, err error) + // ListForSub 子设备 + ListForSub(ctx context.Context, in *model.ListForSubInput) (out *model.ListDeviceForPageOutput, err error) + // CheckBind 检查网关、子设备绑定关系 + CheckBind(ctx context.Context, in *model.CheckBindInput) (bool, error) + // DelSub 子设备删除 + DelSub(ctx context.Context, key string) (err error) + // AuthInfo 获取认证信息 + AuthInfo(ctx context.Context, in *model.AuthInfoInput) (*model.AuthInfoOutput, error) + // GetDeviceOnlineTimeOut 获取设备在线超时时长 + GetDeviceOnlineTimeOut(ctx context.Context, deviceKey string) (timeOut int) + // ExportDevices 导出设备 + ExportDevices(ctx context.Context, req *product.ExportDevicesReq) (res product.ExportDevicesRes, err error) + // ImportDevices 导入设备 + ImportDevices(ctx context.Context, req *product.ImportDevicesReq) (res product.ImportDevicesRes, err error) + SetDevicesStatus(ctx context.Context, req *product.SetDeviceStatusReq) (res product.SetDeviceStatusRes, err error) + // GetDeviceDataList 获取设备属性聚合数据列表 + GetDeviceDataList(ctx context.Context, in *model.DeviceDataListInput) (out *model.DeviceDataListOutput, err error) + // GetAllForProduct 获取指定产品所有设备 + GetAllForProduct(ctx context.Context, productKey string) (list []*entity.DevDevice, err error) + // CacheDeviceDetailList 缓存所有设备详情数据 + CacheDeviceDetailList(ctx context.Context) (err error) } ) var ( - localDevDevice IDevDevice - localDevDeviceLog IDevDeviceLog - localDevDeviceTag IDevDeviceTag - localDevProduct IDevProduct - localDevTSLDataType IDevTSLDataType - localDevTSLFunction IDevTSLFunction - localDevTSLTag IDevTSLTag - localDevCategory IDevCategory - localDevDataReport IDevDataReport - localDevTSLEvent IDevTSLEvent - localDevTSLProperty IDevTSLProperty + localDevCategory IDevCategory + localDevTSLParse IDevTSLParse + localDevTSLTag IDevTSLTag + localDevDataReport IDevDataReport + localDevDevice IDevDevice + localDevDeviceTree IDevDeviceTree + localDevInit IDevInit + localDevTSLImport IDevTSLImport + localDevDeviceFunction IDevDeviceFunction + localDevDeviceLog IDevDeviceLog + localDevDeviceProperty IDevDeviceProperty + localDevProduct IDevProduct + localDevTSLEvent IDevTSLEvent + localDevDeviceTag IDevDeviceTag + localDevTSLDataType IDevTSLDataType + localDevTSLFunction IDevTSLFunction + localDevTSLProperty IDevTSLProperty ) -func DevCategory() IDevCategory { - if localDevCategory == nil { - panic("implement not found for interface IDevCategory, forgot register?") +func DevDeviceTag() IDevDeviceTag { + if localDevDeviceTag == nil { + panic("implement not found for interface IDevDeviceTag, forgot register?") } - return localDevCategory + return localDevDeviceTag } -func RegisterDevCategory(i IDevCategory) { - localDevCategory = i +func RegisterDevDeviceTag(i IDevDeviceTag) { + localDevDeviceTag = i } -func DevDataReport() IDevDataReport { - if localDevDataReport == nil { - panic("implement not found for interface IDevDataReport, forgot register?") +func DevTSLDataType() IDevTSLDataType { + if localDevTSLDataType == nil { + panic("implement not found for interface IDevTSLDataType, forgot register?") } - return localDevDataReport + return localDevTSLDataType } -func RegisterDevDataReport(i IDevDataReport) { - localDevDataReport = i +func RegisterDevTSLDataType(i IDevTSLDataType) { + localDevTSLDataType = i } -func DevTSLEvent() IDevTSLEvent { - if localDevTSLEvent == nil { - panic("implement not found for interface IDevTSLEvent, forgot register?") +func DevTSLFunction() IDevTSLFunction { + if localDevTSLFunction == nil { + panic("implement not found for interface IDevTSLFunction, forgot register?") } - return localDevTSLEvent + return localDevTSLFunction } -func RegisterDevTSLEvent(i IDevTSLEvent) { - localDevTSLEvent = i +func RegisterDevTSLFunction(i IDevTSLFunction) { + localDevTSLFunction = i } func DevTSLProperty() IDevTSLProperty { @@ -154,15 +267,26 @@ func RegisterDevTSLProperty(i IDevTSLProperty) { localDevTSLProperty = i } -func DevTSLFunction() IDevTSLFunction { - if localDevTSLFunction == nil { - panic("implement not found for interface IDevTSLFunction, forgot register?") +func DevCategory() IDevCategory { + if localDevCategory == nil { + panic("implement not found for interface IDevCategory, forgot register?") } - return localDevTSLFunction + return localDevCategory } -func RegisterDevTSLFunction(i IDevTSLFunction) { - localDevTSLFunction = i +func RegisterDevCategory(i IDevCategory) { + localDevCategory = i +} + +func DevTSLParse() IDevTSLParse { + if localDevTSLParse == nil { + panic("implement not found for interface IDevTSLParse, forgot register?") + } + return localDevTSLParse +} + +func RegisterDevTSLParse(i IDevTSLParse) { + localDevTSLParse = i } func DevTSLTag() IDevTSLTag { @@ -176,6 +300,17 @@ func RegisterDevTSLTag(i IDevTSLTag) { localDevTSLTag = i } +func DevDataReport() IDevDataReport { + if localDevDataReport == nil { + panic("implement not found for interface IDevDataReport, forgot register?") + } + return localDevDataReport +} + +func RegisterDevDataReport(i IDevDataReport) { + localDevDataReport = i +} + func DevDevice() IDevDevice { if localDevDevice == nil { panic("implement not found for interface IDevDevice, forgot register?") @@ -187,6 +322,39 @@ func RegisterDevDevice(i IDevDevice) { localDevDevice = i } +func DevDeviceTree() IDevDeviceTree { + if localDevDeviceTree == nil { + panic("implement not found for interface IDevDeviceTree, forgot register?") + } + return localDevDeviceTree +} + +func RegisterDevDeviceTree(i IDevDeviceTree) { + localDevDeviceTree = i +} + +func DevInit() IDevInit { + if localDevInit == nil { + panic("implement not found for interface IDevInit, forgot register?") + } + return localDevInit +} + +func RegisterDevInit(i IDevInit) { + localDevInit = i +} + +func DevDeviceFunction() IDevDeviceFunction { + if localDevDeviceFunction == nil { + panic("implement not found for interface IDevDeviceFunction, forgot register?") + } + return localDevDeviceFunction +} + +func RegisterDevDeviceFunction(i IDevDeviceFunction) { + localDevDeviceFunction = i +} + func DevDeviceLog() IDevDeviceLog { if localDevDeviceLog == nil { panic("implement not found for interface IDevDeviceLog, forgot register?") @@ -198,15 +366,15 @@ func RegisterDevDeviceLog(i IDevDeviceLog) { localDevDeviceLog = i } -func DevDeviceTag() IDevDeviceTag { - if localDevDeviceTag == nil { - panic("implement not found for interface IDevDeviceTag, forgot register?") +func DevDeviceProperty() IDevDeviceProperty { + if localDevDeviceProperty == nil { + panic("implement not found for interface IDevDeviceProperty, forgot register?") } - return localDevDeviceTag + return localDevDeviceProperty } -func RegisterDevDeviceTag(i IDevDeviceTag) { - localDevDeviceTag = i +func RegisterDevDeviceProperty(i IDevDeviceProperty) { + localDevDeviceProperty = i } func DevProduct() IDevProduct { @@ -220,13 +388,24 @@ func RegisterDevProduct(i IDevProduct) { localDevProduct = i } -func DevTSLDataType() IDevTSLDataType { - if localDevTSLDataType == nil { - panic("implement not found for interface IDevTSLDataType, forgot register?") +func DevTSLEvent() IDevTSLEvent { + if localDevTSLEvent == nil { + panic("implement not found for interface IDevTSLEvent, forgot register?") } - return localDevTSLDataType + return localDevTSLEvent } -func RegisterDevTSLDataType(i IDevTSLDataType) { - localDevTSLDataType = i +func RegisterDevTSLEvent(i IDevTSLEvent) { + localDevTSLEvent = i +} + +func DevTSLImport() IDevTSLImport { + if localDevTSLImport == nil { + panic("implement not found for interface IDevTSLImport, forgot register?") + } + return localDevTSLImport +} + +func RegisterDevTSLImport(i IDevTSLImport) { + localDevTSLImport = i } diff --git a/internal/service/system.go b/internal/service/system.go index 1c71df4..0fcf043 100644 --- a/internal/service/system.go +++ b/internal/service/system.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -7,260 +7,470 @@ package service import ( "context" - "github.com/gogf/gf/v2/database/gdb" - "github.com/sagoo-cloud/sagooiot/api/v1/system" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/model/entity" "net/url" + "sagooiot/api/v1/system" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/pkg/gftoken" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/net/ghttp" - "github.com/tiger1103/gfast-token/gftoken" ) type ( - ILogin interface { - Login(ctx context.Context, verifyKey string, captcha string, userName string, password string) (loginUserOut *model.LoginUserOut, token string, err error) - LoginOut(ctx context.Context) (err error) - } - ISysJob interface { - JobList(ctx context.Context, input *model.GetJobListInput) (total int, out []*model.SysJobOut, err error) - GetJobs(ctx context.Context) (jobs []*model.SysJobOut, err error) - AddJob(ctx context.Context, input *model.SysJobAddInput) (err error) - GetJobInfoById(ctx context.Context, id int) (job *model.SysJobOut, err error) - EditJob(ctx context.Context, input *model.SysJobEditInput) error - JobStart(ctx context.Context, job *model.SysJobOut) error - JobStartMult(ctx context.Context, jobs []*model.SysJobOut) error - JobStop(ctx context.Context, job *model.SysJobOut) error - JobRun(ctx context.Context, job *model.SysJobOut) error - DeleteJobByIds(ctx context.Context, ids []int) (err error) - WithValue(ctx context.Context, value string) context.Context - Value(ctx context.Context) uint64 + ISysLoginLog interface { + Invoke(ctx context.Context, data *model.LoginLogParams) + // Add 记录登录日志 + Add(ctx context.Context, params *model.LoginLogParams) + // GetList 获取登录日志数据列表 + GetList(ctx context.Context, req *model.SysLoginLogInput) (total, page int, list []*model.SysLoginLogOut, err error) + // Detail 登录日志详情 + Detail(ctx context.Context, infoId int) (entity *entity.SysLoginLog, err error) + // Del 根据ID删除登录日志 + Del(ctx context.Context, infoIds []int) (err error) + // Export 导出登录日志列表 + Export(ctx context.Context, req *model.SysLoginLogInput) (err error) } ISysOperLog interface { + // GetList 获取操作日志数据列表 GetList(ctx context.Context, input *model.SysOperLogDoInput) (total int, out []*model.SysOperLogOut, err error) Invoke(ctx context.Context, userId int, url *url.URL, param g.Map, method string, clientIp string, res map[string]interface{}, err error) + // Add 添加操作日志 Add(ctx context.Context, userId int, url *url.URL, param g.Map, method string, clientIp string, res map[string]interface{}, erro error) (err error) + AnalysisLog(ctx context.Context) (data entity.SysOperLog) + // RealWrite 真实写入 + RealWrite(ctx context.Context, log entity.SysOperLog) (err error) + // Detail 操作日志详情 Detail(ctx context.Context, operId int) (entity *entity.SysOperLog, err error) + // Del 根据ID删除操作日志 Del(ctx context.Context, operIds []int) (err error) ClearOperationLogByDays(ctx context.Context, days int) (err error) } - ISystemPluginsConfig interface { - GetPluginsConfigList(ctx context.Context, in *model.GetPluginsConfigListInput) (total, page int, list []*model.PluginsConfigOutput, err error) - GetPluginsConfigById(ctx context.Context, id int) (out *model.PluginsConfigOutput, err error) - GetPluginsConfigByName(ctx context.Context, types, name string) (out *model.PluginsConfigOutput, err error) - AddPluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) - EditPluginsConfig(ctx context.Context, in model.PluginsConfigEditInput) (err error) - SavePluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) - DeletePluginsConfig(ctx context.Context, Ids []int) (err error) - UpdateAllPluginsConfigCache() (err error) - GetPluginsConfigData(pluginType, pluginName string) (res map[interface{}]interface{}, err error) - } - ISysUserRole interface { - GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserRole, err error) - } - ICaptcha interface { - GetVerifyImgString(ctx context.Context) (idKeyC string, base64stringC string, err error) - VerifyString(id, answer string) bool - } - ISysDept interface { - GetTree(ctx context.Context, deptName string, status int) (out []*model.DeptOut, err error) - GetData(ctx context.Context, deptName string, status int) (data []*model.DeptOut, err error) - Add(ctx context.Context, input *model.AddDeptInput) (err error) - Edit(ctx context.Context, input *model.EditDeptInput) (err error) - Detail(ctx context.Context, deptId int64) (entity *entity.SysDept, err error) - Del(ctx context.Context, deptId int64) (err error) - GetAll(ctx context.Context) (data []*entity.SysDept, err error) - GetFromCache(ctx context.Context) (list []*entity.SysDept, err error) - FindSonByParentId(deptList []*entity.SysDept, deptId int64) []*entity.SysDept - } - ISysNotifications interface { - GetSysNotificationsList(ctx context.Context, input *model.GetNotificationsListInput) (total, page int, list []*model.NotificationsOut, err error) - GetSysNotificationsById(ctx context.Context, id int) (out *model.NotificationsRes, err error) - AddSysNotifications(ctx context.Context, in model.NotificationsAddInput) (err error) - EditSysNotifications(ctx context.Context, in model.NotificationsEditInput) (err error) - DeleteSysNotifications(ctx context.Context, in *system.DeleteNotificationsReq) (err error) - } - ISysPlugins interface { - GetSysPluginsList(ctx context.Context, in *model.GetSysPluginsListInput) (total, page int, list []*model.SysPluginsOutput, err error) - GetSysPluginsById(ctx context.Context, id int) (out *model.SysPluginsOutput, err error) - AddSysPlugins(ctx context.Context, in model.SysPluginsAddInput) (err error) - EditSysPlugins(ctx context.Context, in model.SysPluginsEditInput) (err error) - DeleteSysPlugins(ctx context.Context, Ids []int) (err error) - SaveSysPlugins(ctx context.Context, in model.SysPluginsAddInput) (err error) - EditStatus(ctx context.Context, id int, status int) (err error) - GetSysPluginsTypesAll(ctx context.Context, types string) (out []*model.SysPluginsInfoOut, err error) + ISysToken interface { + GenerateToken(ctx context.Context, key string, data interface{}) (keys string, err error) + ParseToken(r *ghttp.Request) (*gftoken.CustomClaims, error) } ISysApi interface { + // GetInfoByIds 根据接口APIID数组获取接口信息 GetInfoByIds(ctx context.Context, ids []int) (data []*entity.SysApi, err error) + // GetApiByMenuId 根据ApiID获取接口信息 GetApiByMenuId(ctx context.Context, apiId int) (data []*entity.SysApi, err error) + // GetInfoById 根据ID获取API GetInfoById(ctx context.Context, id int) (entity *entity.SysApi, err error) - GetApiAll(ctx context.Context) (data []*entity.SysApi, err error) + // GetApiAll 获取所有接口 + GetApiAll(ctx context.Context, method string) (data []*entity.SysApi, err error) + // GetApiTree 获取Api数结构数据 GetApiTree(ctx context.Context, name string, address string, status int, types int) (out []*model.SysApiTreeOut, err error) + // Add 添加Api列表 Add(ctx context.Context, input *model.AddApiInput) (err error) + // Detail Api列表详情 Detail(ctx context.Context, id int) (out *model.SysApiOut, err error) + AddMenuApi(ctx context.Context, addPageSource string, apiIds []int, menuIds []int) (err error) + // Edit 修改Api列表 Edit(ctx context.Context, input *model.EditApiInput) (err error) + // Del 根据ID删除Api列表信息 Del(ctx context.Context, Id int) (err error) + // EditStatus 修改状态 EditStatus(ctx context.Context, id int, status int) (err error) + // GetInfoByAddress 根据Address获取API GetInfoByAddress(ctx context.Context, address string) (entity *entity.SysApi, err error) + // GetInfoByNameAndTypes 根据名字和类型获取API + GetInfoByNameAndTypes(ctx context.Context, name string, types int) (entity *entity.SysApi, err error) + // ImportApiFile 导入API文件 + ImportApiFile(ctx context.Context) (err error) } - ISysLoginLog interface { - Invoke(ctx context.Context, data *model.LoginLogParams) - Add(ctx context.Context, params *model.LoginLogParams) - GetList(ctx context.Context, req *model.SysLoginLogInput) (total, page int, list []*model.SysLoginLogOut, err error) - Detail(ctx context.Context, infoId int) (entity *entity.SysLoginLog, err error) - Del(ctx context.Context, infoIds []int) (err error) + ISysJob interface { + // JobList 获取任务列表 + JobList(ctx context.Context, input *model.GetJobListInput) (total int, out []*model.SysJobOut, err error) + // GetJobs 获取已开启执行的任务 + GetJobs(ctx context.Context) (jobs []*model.SysJobOut, err error) + // GetJobFuns 获取任务可用方法列表 + GetJobFuns(ctx context.Context) (jobsList []*model.SysJobFunListOut, err error) + AddJob(ctx context.Context, input *model.SysJobAddInput) (err error) + GetJobInfoById(ctx context.Context, id int) (job *model.SysJobOut, err error) + EditJob(ctx context.Context, input *model.SysJobEditInput) error + // JobStart 启动任务 + JobStart(ctx context.Context, job *model.SysJobOut) error + // JobStartMult 批量启动任务 + JobStartMult(ctx context.Context, jobsList []*model.SysJobOut) error + // JobStop 停止任务 + JobStop(ctx context.Context, job *model.SysJobOut) (err error) + // JobRun 执行任务 + JobRun(ctx context.Context, job *model.SysJobOut) (err error) + // DeleteJobByIds 删除任务 + DeleteJobByIds(ctx context.Context, ids []int) (err error) + WithValue(ctx context.Context, value string) context.Context + Value(ctx context.Context) uint64 } ISysMenuApi interface { + // MenuApiList 根据菜单ID获取API列表 + MenuApiList(ctx context.Context, menuId int) (out []*model.SysApiAllOut, err error) + // GetInfoByIds 根据IDS数组获取菜单信息 GetInfoByIds(ctx context.Context, ids []int) (data []*entity.SysMenuApi, err error) + // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuApi, err error) + // GetInfoByApiId 根据接口ID数组获取菜单信息 GetInfoByApiId(ctx context.Context, apiId int) (data []*entity.SysMenuApi, err error) + // GetAll 获取所有信息 + GetAll(ctx context.Context) (data []*entity.SysMenuApi, err error) + // GetInfoByMenuId 根据菜单ID获取菜单信息 + GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuApi, err error) + } + ISysMenuColumn interface { + // GetList 获取全部菜单列表数据 + GetList(ctx context.Context, input *model.MenuColumnDoInput) (data []*model.UserMenuColumnOut, err error) + // GetData 执行获取数据操作 + GetData(ctx context.Context, input *model.MenuColumnDoInput) (data []model.UserMenuColumnOut, err error) + // Add 添加菜单列表 + Add(ctx context.Context, input *model.AddMenuColumnInput) (err error) + // Detail 菜单列表详情 + Detail(ctx context.Context, Id int64) (entity *entity.SysMenuColumn, err error) + // Edit 修改菜单列表 + Edit(ctx context.Context, input *model.EditMenuColumnInput) (err error) + // Del 根据ID删除菜单列表信息 + Del(ctx context.Context, Id int64) (err error) + // EditStatus 修改状态 + EditStatus(ctx context.Context, id int, menuId int, status int) (err error) + // GetInfoByColumnIds 根据列表ID数组获取菜单信息 + GetInfoByColumnIds(ctx context.Context, ids []int) (data []*entity.SysMenuColumn, err error) + // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 + GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuColumn, err error) + // GetInfoByMenuId 根据菜单ID获取菜单信息 + GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuColumn, err error) + // GetAll 获取所有的列表信息 + GetAll(ctx context.Context) (data []*entity.SysMenuColumn, err error) } ISysOrganization interface { + // GetTree 获取组织数据 GetTree(ctx context.Context, name string, status int) (data []*model.OrganizationOut, err error) + // GetData 执行获取数据操作 GetData(ctx context.Context, name string, status int) (data []*model.OrganizationOut, err error) + // Add 添加 Add(ctx context.Context, input *model.AddOrganizationInput) (err error) + // Edit 修改组织 Edit(ctx context.Context, input *model.EditOrganizationInput) (err error) + // Detail 组织详情 Detail(ctx context.Context, id int64) (entity *entity.SysOrganization, err error) + // Del 根据ID删除组织信息 Del(ctx context.Context, id int64) (err error) + // GetAll 获取全部组织数据 GetAll(ctx context.Context) (data []*entity.SysOrganization, err error) + // Count 获取组织数量 Count(ctx context.Context) (count int, err error) } + ISystemPluginsConfig interface { + // GetPluginsConfigList 获取列表数据 + GetPluginsConfigList(ctx context.Context, in *model.GetPluginsConfigListInput) (total, page int, list []*model.PluginsConfigOutput, err error) + // GetPluginsConfigById 获取指定ID数据 + GetPluginsConfigById(ctx context.Context, id int) (out *model.PluginsConfigOutput, err error) + // GetPluginsConfigByName 获取指定ID数据 + GetPluginsConfigByName(ctx context.Context, types, name string) (out *model.PluginsConfigOutput, err error) + // AddPluginsConfig 添加数据 + AddPluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) + // EditPluginsConfig 修改数据 + EditPluginsConfig(ctx context.Context, in model.PluginsConfigEditInput) (err error) + // SavePluginsConfig 更新数据,有数据就修改,没有数据就添加 + SavePluginsConfig(ctx context.Context, in model.PluginsConfigAddInput) (err error) + // DeletePluginsConfig 删除数据 + DeletePluginsConfig(ctx context.Context, Ids []int) (err error) + // UpdateAllPluginsConfigCache 将插件数据更新到缓存 + UpdateAllPluginsConfigCache(ctx context.Context) (err error) + // GetPluginsConfigData 获取列表数据 + GetPluginsConfigData(pluginType, pluginName string) (res map[interface{}]interface{}, err error) + } ISysPost interface { + // GetTree 获取全部岗位数据 GetTree(ctx context.Context, postName string, postCode string, status int) (data []*model.PostOut, err error) + // Add 添加岗位 Add(ctx context.Context, input *model.AddPostInput) (err error) + // Edit 修改岗位 Edit(ctx context.Context, input *model.EditPostInput) (err error) + // Detail 岗位详情 Detail(ctx context.Context, postId int64) (entity *entity.SysPost, err error) + // GetData 执行获取数据操作 GetData(ctx context.Context, postName string, postCode string, status int) (data []*model.PostOut, err error) + // Del 根据ID删除岗位信息 Del(ctx context.Context, postId int64) (err error) - GetUsedPost(ctx context.Context) (list []*model.DetailPostRes, err error) + // GetUsedPost 获取正常状态的岗位 + GetUsedPost(ctx context.Context) (list []*model.DetailPostOut, err error) } ISysRole interface { + // GetAll 获取所有的角色 GetAll(ctx context.Context) (entity []*entity.SysRole, err error) GetTree(ctx context.Context, name string, status int) (out []*model.RoleTreeOut, err error) + // Add 添加 Add(ctx context.Context, input *model.AddRoleInput) (err error) + // Edit 编辑 Edit(ctx context.Context, input *model.EditRoleInput) (err error) + // GetInfoById 根据ID获取角色信息 GetInfoById(ctx context.Context, id uint) (entity *entity.SysRole, err error) + // DelInfoById 根据ID删除角色信息 DelInfoById(ctx context.Context, id uint) (err error) - GetRoleList(ctx context.Context) (list []*model.RoleInfoRes, err error) + // GetRoleList 获取角色列表 + GetRoleList(ctx context.Context) (list []*model.RoleInfoOut, err error) + // GetInfoByIds 根据ID数组获取角色信息 GetInfoByIds(ctx context.Context, id []int) (entity []*entity.SysRole, err error) + // DataScope 角色数据授权 DataScope(ctx context.Context, id int, dataScope uint, deptIds []int64) (err error) GetAuthorizeById(ctx context.Context, id int) (menuIds []string, menuButtonIds []string, menuColumnIds []string, menuApiIds []string, err error) } - ISysToken interface { - GenerateToken(ctx context.Context, key string, data interface{}) (keys string, err error) - ParseToken(r *ghttp.Request) (*gftoken.CustomClaims, error) + ILogin interface { + // Login 登录 + Login(ctx context.Context, verifyKey string, captcha string, userName string, password string) (loginUserOut *model.LoginUserOut, token string, isChangePassword int, err error) + // CheckPwdErrorNum 验证密码错误次数 + CheckPwdErrorNum(ctx context.Context, userName string) (err error) + IsChangePwd(ctx context.Context, userName string) (isChangePwd int) + // GenUserToken 生成用户TOKEN + GenUserToken(ctx context.Context, isSecurityControlEnabled string, ip string, userAgent string, userInfo *entity.SysUser, logMoudel string) (loginUserOut *model.LoginUserOut, token string, err error) + LoginOut(ctx context.Context) (err error) } - ISysUser interface { - GetUserByUsername(ctx context.Context, userName string) (data *entity.SysUser, err error) - GetAdminUserByUsernamePassword(ctx context.Context, userName string, password string) (user *entity.SysUser, err error) - UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error) - UserList(ctx context.Context, input *model.UserListDoInput) (total int, out []*model.UserListOut, err error) - Add(ctx context.Context, input *model.AddUserInput) (err error) - Edit(ctx context.Context, input *model.EditUserInput) (err error) - GetUserById(ctx context.Context, id uint) (out *model.UserInfoOut, err error) - DelInfoById(ctx context.Context, id uint) (err error) - ResetPassword(ctx context.Context, id uint, userPassword string) (err error) - EditUserStatus(ctx context.Context, id uint, status uint) (err error) - GetUserByIds(ctx context.Context, id []int) (data []*entity.SysUser, err error) - GetAll(ctx context.Context) (data []*entity.SysUser, err error) - CurrentUser(ctx context.Context) (userInfoOut *model.UserInfoOut, menuTreeOut []*model.UserMenuTreeOut, err error) - EditUserAvatar(ctx context.Context, id uint, avatar string) (err error) - EditUserInfo(ctx context.Context, input *model.EditUserInfoInput) (err error) + ISysUserOnline interface { + Invoke(ctx context.Context, data *entity.SysUserOnline) + // Add 记录用户在线 + Add(ctx context.Context, data *entity.SysUserOnline) + // DelByToken 根据token删除信息 + DelByToken(ctx context.Context, token string) (err error) + // GetInfoByToken 根据token获取 + GetInfoByToken(ctx context.Context, token string) (data *entity.SysUserOnline, err error) + // DelByIds 根据IDS删除信息 + DelByIds(ctx context.Context, ids []int) (err error) + GetAll(ctx context.Context) (data []*entity.SysUserOnline, err error) + // UserOnlineList 在线用户列表 + UserOnlineList(ctx context.Context, input *model.UserOnlineDoListInput) (total int, out []*model.UserOnlineListOut, err error) + UserOnlineStrongBack(ctx context.Context, id int) (err error) } ISysAuthorize interface { AuthorizeQuery(ctx context.Context, itemsType string, menuIds []int) (out []*model.AuthorizeQueryTreeOut, err error) + // GetInfoByRoleId 根据角色ID获取权限信息 GetInfoByRoleId(ctx context.Context, roleId int) (data []*entity.SysAuthorize, err error) + // GetInfoByRoleIds 根据角色ID数组获取权限信息 GetInfoByRoleIds(ctx context.Context, roleIds []int) (data []*entity.SysAuthorize, err error) + // GetInfoByRoleIdsAndItemsType 根据角色ID和项目类型获取权限信息 GetInfoByRoleIdsAndItemsType(ctx context.Context, roleIds []int, itemsType string) (data []*entity.SysAuthorize, err error) DelByRoleId(ctx context.Context, roleId int) (err error) Add(ctx context.Context, authorize []*entity.SysAuthorize) (err error) AddAuthorize(ctx context.Context, roleId int, menuIds []string, buttonIds []string, columnIds []string, apiIds []string) (err error) IsAllowAuthorize(ctx context.Context, roleId int) (isAllow bool, err error) + // InitAuthorize 初始化系统权限 InitAuthorize(ctx context.Context) (err error) - FilterDataByPermissions(ctx context.Context, model *gdb.Model) (*gdb.Model, error) - GetDataWhere(ctx context.Context) (where g.Map, err error) } - ISysUserPost interface { - GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserPost, err error) + ISysCertificate interface { + // GetList 获取列表数据 + GetList(ctx context.Context, input *model.SysCertificateListInput) (total, page int, out []*model.SysCertificateListOut, err error) + // GetInfoById 获取指定ID数据 + GetInfoById(ctx context.Context, id int) (out *model.SysCertificateListOut, err error) + // Add 添加数据 + Add(ctx context.Context, input *model.AddSysCertificateListInput) (err error) + // Edit 修改数据 + Edit(ctx context.Context, input *model.EditSysCertificateListInput) (err error) + // Delete 删除数据 + Delete(ctx context.Context, id int) (err error) + // EditStatus 更新状态 + EditStatus(ctx context.Context, id int, status int) (err error) + // GetAll 获取所有证书 + GetAll(ctx context.Context) (out []*entity.SysCertificate, err error) } - ISysUserOnline interface { - Invoke(ctx context.Context, data *entity.SysUserOnline) - Add(ctx context.Context, data *entity.SysUserOnline) - DelByToken(ctx context.Context, token string) (err error) - GetInfoByToken(ctx context.Context, token string) (data *entity.SysUserOnline, err error) - DelByIds(ctx context.Context, ids []uint) (err error) - GetAll(ctx context.Context) (data []*entity.SysUserOnline, err error) - UserOnlineList(ctx context.Context, input *model.UserOnlineDoListInput) (total int, out []*model.UserOnlineListOut, err error) - UserOnlineStrongBack(ctx context.Context, id int) (err error) + ISysMenu interface { + // GetAll 获取全部菜单数据 + GetAll(ctx context.Context) (data []*entity.SysMenu, err error) + // GetTree 获取菜单数据 + GetTree(ctx context.Context, title string, status int) (data []*model.SysMenuOut, err error) + // Add 添加菜单 + Add(ctx context.Context, input *model.AddMenuInput) (err error) + // Detail 菜单详情 + Detail(ctx context.Context, menuId int64) (entity *entity.SysMenu, err error) + // Edit 修改菜单 + Edit(ctx context.Context, input *model.EditMenuInput) (err error) + // Del 根据ID删除菜单信息 + Del(ctx context.Context, menuId int64) (err error) + // GetData 执行获取数据操作 + GetData(ctx context.Context, title string, status int) (data []*model.SysMenuOut, err error) + // GetInfoByMenuIds 根据菜单ID数组获取菜单信息 + GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenu, err error) + GetInfoById(ctx context.Context, id int) (data *entity.SysMenu, err error) } ISysMenuButton interface { - GetList(ctx context.Context, status int, name string, menuId int) (data []model.UserMenuButtonRes, err error) - GetData(ctx context.Context, status int, name string, menuId int, menuButton []model.UserMenuButtonRes) (data []model.UserMenuButtonRes, err error) + // GetList 获取全部菜单按钮数据 + GetList(ctx context.Context, status int, name string, menuId int) (data []*model.UserMenuButtonOut, err error) + // GetData 执行获取数据操作 + GetData(ctx context.Context, status int, name string, menuId int) (data []model.UserMenuButtonOut, err error) + // Add 添加菜单按钮 Add(ctx context.Context, input *model.AddMenuButtonInput) (err error) + // Detail 菜单按钮详情 Detail(ctx context.Context, Id int64) (entity *entity.SysMenuButton, err error) + // Edit 修改菜单按钮 Edit(ctx context.Context, input *model.EditMenuButtonInput) (err error) + // Del 根据ID删除菜单按钮信息 Del(ctx context.Context, id int64) (err error) + // GetInfoByButtonIds 根据按钮ID数组获取菜单按钮信息 GetInfoByButtonIds(ctx context.Context, ids []int) (data []*entity.SysMenuButton, err error) + // GetInfoByMenuIds 根据菜单ID数组获取菜单按钮信息 GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuButton, err error) + // GetInfoByMenuId 根据菜单ID数组获取菜单按钮信息 GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuButton, err error) + // GetAll 获取所有的按钮信息 GetAll(ctx context.Context) (data []*entity.SysMenuButton, err error) + // EditStatus 修改状态 EditStatus(ctx context.Context, id int, menuId int, status int) (err error) } - ISysMenuColumn interface { - GetList(ctx context.Context, input *model.MenuColumnDoInput) (data []model.UserMenuColumnRes, err error) - GetData(ctx context.Context, input *model.MenuColumnDoInput, menuColumn []model.UserMenuColumnRes) (data []model.UserMenuColumnRes, err error) - Add(ctx context.Context, input *model.AddMenuColumnInput) (err error) - Detail(ctx context.Context, Id int64) (entity *entity.SysMenuColumn, err error) - Edit(ctx context.Context, input *model.EditMenuColumnInput) (err error) - Del(ctx context.Context, Id int64) (err error) - EditStatus(ctx context.Context, id int, menuId int, status int) (err error) - GetInfoByColumnIds(ctx context.Context, ids []int) (data []*entity.SysMenuColumn, err error) - GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenuColumn, err error) - GetInfoByMenuId(ctx context.Context, menuId int) (data []*entity.SysMenuColumn, err error) - GetAll(ctx context.Context) (data []*entity.SysMenuColumn, err error) - } ISysRoleDept interface { + // GetInfoByRoleId 根据角色ID获取信息 GetInfoByRoleId(ctx context.Context, roleId int) (data []*entity.SysRoleDept, err error) } - ISysMenu interface { - GetAll(ctx context.Context) (data []*entity.SysMenu, err error) - GetTree(ctx context.Context, title string, status int) (data []*model.SysMenuOut, err error) - Add(ctx context.Context, input *model.AddMenuInput) (err error) - Detail(ctx context.Context, menuId int64) (entity *entity.SysMenu, err error) - Edit(ctx context.Context, input *model.EditMenuInput) (err error) - Del(ctx context.Context, menuId int64) (err error) - GetData(ctx context.Context, title string, status int) (data []*model.SysMenuOut, err error) - GetInfoByMenuIds(ctx context.Context, menuIds []int) (data []*entity.SysMenu, err error) - GetInfoById(ctx context.Context, id int) (data *entity.SysMenu, err error) + ISysUser interface { + // GetUserByUsername 通过用户名获取用户信息 + GetUserByUsername(ctx context.Context, userName string) (data *entity.SysUser, err error) + // GetAdminUserByUsernamePassword 根据用户名和密码获取用户信息 + GetAdminUserByUsernamePassword(ctx context.Context, userName string, password string) (user *entity.SysUser, err error) + // UpdateLoginInfo 更新用户登录信息 + UpdateLoginInfo(ctx context.Context, id uint64, ip string) (err error) + // UserList 用户列表 + UserList(ctx context.Context, input *model.UserListDoInput) (total int, out []*model.UserListOut, err error) + // Add 添加 + Add(ctx context.Context, input *model.AddUserInput) (err error) + Edit(ctx context.Context, input *model.EditUserInput) (err error) + // GetUserById 根据ID获取用户信息 + GetUserById(ctx context.Context, id uint) (out *model.UserInfoOut, err error) + // DelInfoById 根据ID删除信息 + DelInfoById(ctx context.Context, id uint) (err error) + // ResetPassword 重置密码 + ResetPassword(ctx context.Context, id uint, userPassword string) (err error) + // EditUserStatus 修改用户状态 + EditUserStatus(ctx context.Context, id uint, status uint) (err error) + // GetUserByIds 根据ID数据获取用户信息 + GetUserByIds(ctx context.Context, id []int) (data []*entity.SysUser, err error) + // GetAll 获取所有用户信息 + GetAll(ctx context.Context) (data []*entity.SysUser, err error) + CurrentUser(ctx context.Context) (userInfoOut *model.UserInfoOut, menuTreeOut []*model.UserMenuTreeOut, err error) + // EditUserAvatar 修改用户头像 + EditUserAvatar(ctx context.Context, id uint, avatar string) (err error) + // EditUserInfo 修改用户个人资料 + EditUserInfo(ctx context.Context, input *model.EditUserInfoInput) (err error) + // CheckPassword 校验用户密码 + CheckPassword(ctx context.Context, userPassword string) (err error) + // EditPassword 修改密码 + EditPassword(ctx context.Context, userName string, oldUserPassword string, userPassword string) (err error) + } + ISysUserRole interface { + // GetInfoByUserId 根据用户ID获取信息 + GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserRole, err error) + // BindUserAndRole 添加用户与角色绑定关系 + BindUserAndRole(ctx context.Context, userId int, roleIds []int) (err error) + } + ICaptcha interface { + // GetVerifyImgString 获取字母数字混合验证码 + GetVerifyImgString(ctx context.Context) (idKeyC string, base64stringC string, err error) + // VerifyString 验证输入的验证码是否正确 + VerifyString(id, answer string) bool + } + ISysMessage interface { + // GetList 获取列表数据 + GetList(ctx context.Context, input *model.MessageListDoInput) (total int, out []*model.MessageListOut, err error) + // Add 新增 + Add(ctx context.Context, messageInfo *model.AddMessageInput) (err error) + // GetUnReadMessageAll 获取所有未读消息 + GetUnReadMessageAll(ctx context.Context, input *model.MessageListDoInput) (total int, out []*model.MessageListOut, err error) + // GetUnReadMessageCount 获取所有未读消息数量 + GetUnReadMessageCount(ctx context.Context) (out int, err error) + // DelMessage 删除消息 + DelMessage(ctx context.Context, ids []int) (err error) + // ClearMessage 一键清空消息 + ClearMessage(ctx context.Context) (err error) + // ReadMessage 阅读消息 + ReadMessage(ctx context.Context, id int) (err error) + // ReadMessageAll 全部阅读消息 + ReadMessageAll(ctx context.Context) (err error) + // GetUnReadMessageLast 获取用户最后一条未读消息 + GetUnReadMessageLast(ctx context.Context, userId int) (out []*model.MessageListOut, err error) + } + ISysNotifications interface { + // GetSysNotificationsList 获取列表数据 + GetSysNotificationsList(ctx context.Context, input *model.GetNotificationsListInput) (total, page int, list []*model.NotificationsOut, err error) + // GetSysNotificationsById 获取指定ID数据 + GetSysNotificationsById(ctx context.Context, id int) (out *model.NotificationsRes, err error) + // AddSysNotifications 添加数据 + AddSysNotifications(ctx context.Context, in model.NotificationsAddInput) (err error) + // EditSysNotifications 修改数据 + EditSysNotifications(ctx context.Context, in model.NotificationsEditInput) (err error) + // DeleteSysNotifications 删除数据 + DeleteSysNotifications(ctx context.Context, in *system.DeleteNotificationsReq) (err error) + } + ISysPlugins interface { + // GetSysPluginsList 获取列表数据 + GetSysPluginsList(ctx context.Context, in *model.GetSysPluginsListInput) (total, page int, list []*model.GetSysPluginsListOut, err error) + // GetSysPluginsById 获取指定ID数据 + GetSysPluginsById(ctx context.Context, id int) (out *entity.SysPlugins, err error) + // GetSysPluginsByName 根据名称获取插件数据 + GetSysPluginsByName(ctx context.Context, name string) (out *entity.SysPlugins, err error) + // GetSysPluginsByTitle 根据TITLE获取插件数据 + GetSysPluginsByTitle(ctx context.Context, title string) (out *entity.SysPlugins, err error) + // AddSysPlugins 添加数据 + AddSysPlugins(ctx context.Context, file *ghttp.UploadFile) (err error) + // EditSysPlugins 修改数据 + EditSysPlugins(ctx context.Context, input *model.SysPluginsEditInput) (err error) + // DeleteSysPlugins 删除数据 + DeleteSysPlugins(ctx context.Context, ids []int) (err error) + // SaveSysPlugins 存入插件数据,跟据插件类型与名称,数据中只保存一份 + SaveSysPlugins(ctx context.Context, in model.SysPluginsAddInput) (err error) + EditStatus(ctx context.Context, id int, status int) (err error) + // GetSysPluginsTypesAll 获取所有插件的通信方式类型 + GetSysPluginsTypesAll(ctx context.Context, types string) (out []*model.SysPluginsInfoOut, err error) + } + ISysUserPost interface { + // GetInfoByUserId 根据用户ID获取信息 + GetInfoByUserId(ctx context.Context, userId int) (data []*entity.SysUserPost, err error) + // BindUserAndPost 添加用户与岗位绑定关系 + BindUserAndPost(ctx context.Context, userId int, postIds []int) (err error) + } + ISysDept interface { + // GetTree 获取全部部门数据 + GetTree(ctx context.Context, deptName string, status int) (out []*model.DeptOut, err error) + // GetData 执行获取数据操作 + GetData(ctx context.Context, deptName string, status int) (data []*model.DeptOut, err error) + // Add 添加 + Add(ctx context.Context, input *model.AddDeptInput) (err error) + // Edit 修改部门 + Edit(ctx context.Context, input *model.EditDeptInput) (err error) + // Detail 部门详情 + Detail(ctx context.Context, deptId int64) (entity *entity.SysDept, err error) + // Del 根据ID删除部门信息 + Del(ctx context.Context, deptId int64) (err error) + // GetAll 获取全部部门数据 + GetAll(ctx context.Context) (data []*entity.SysDept, err error) + GetFromCache(ctx context.Context) (list []*entity.SysDept, err error) + FindSonByParentId(deptList []*entity.SysDept, deptId int64) []*entity.SysDept + // GetDeptInfosByParentId 根据父ID获取子部门信息 + GetDeptInfosByParentId(ctx context.Context, parentId int) (data []*entity.SysDept, err error) } ) var ( + localSysUserRole ISysUserRole + localCaptcha ICaptcha + localSysAuthorize ISysAuthorize + localSysCertificate ISysCertificate localSysMenu ISysMenu localSysMenuButton ISysMenuButton - localSysMenuColumn ISysMenuColumn localSysRoleDept ISysRoleDept - localCaptcha ICaptcha - localLogin ILogin - localSysJob ISysJob - localSysOperLog ISysOperLog - localSystemPluginsConfig ISystemPluginsConfig - localSysUserRole ISysUserRole - localSysApi ISysApi + localSysUser ISysUser localSysDept ISysDept + localSysMessage ISysMessage localSysNotifications ISysNotifications localSysPlugins ISysPlugins - localSysUser ISysUser - localSysAuthorize ISysAuthorize + localSysUserPost ISysUserPost + localSysApi ISysApi localSysLoginLog ISysLoginLog + localSysOperLog ISysOperLog + localSysToken ISysToken + localSysRole ISysRole + localLogin ILogin + localSysJob ISysJob localSysMenuApi ISysMenuApi + localSysMenuColumn ISysMenuColumn localSysOrganization ISysOrganization + localSystemPluginsConfig ISystemPluginsConfig localSysPost ISysPost - localSysRole ISysRole - localSysToken ISysToken localSysUserOnline ISysUserOnline - localSysUserPost ISysUserPost ) func SysApi() ISysApi { @@ -274,125 +484,125 @@ func RegisterSysApi(i ISysApi) { localSysApi = i } -func SysDept() ISysDept { - if localSysDept == nil { - panic("implement not found for interface ISysDept, forgot register?") +func SysLoginLog() ISysLoginLog { + if localSysLoginLog == nil { + panic("implement not found for interface ISysLoginLog, forgot register?") } - return localSysDept + return localSysLoginLog } -func RegisterSysDept(i ISysDept) { - localSysDept = i +func RegisterSysLoginLog(i ISysLoginLog) { + localSysLoginLog = i } -func SysNotifications() ISysNotifications { - if localSysNotifications == nil { - panic("implement not found for interface ISysNotifications, forgot register?") +func SysOperLog() ISysOperLog { + if localSysOperLog == nil { + panic("implement not found for interface ISysOperLog, forgot register?") } - return localSysNotifications + return localSysOperLog } -func RegisterSysNotifications(i ISysNotifications) { - localSysNotifications = i +func RegisterSysOperLog(i ISysOperLog) { + localSysOperLog = i } -func SysPlugins() ISysPlugins { - if localSysPlugins == nil { - panic("implement not found for interface ISysPlugins, forgot register?") +func SysToken() ISysToken { + if localSysToken == nil { + panic("implement not found for interface ISysToken, forgot register?") } - return localSysPlugins + return localSysToken } -func RegisterSysPlugins(i ISysPlugins) { - localSysPlugins = i +func RegisterSysToken(i ISysToken) { + localSysToken = i } -func SysPost() ISysPost { - if localSysPost == nil { - panic("implement not found for interface ISysPost, forgot register?") +func Login() ILogin { + if localLogin == nil { + panic("implement not found for interface ILogin, forgot register?") } - return localSysPost + return localLogin } -func RegisterSysPost(i ISysPost) { - localSysPost = i +func RegisterLogin(i ILogin) { + localLogin = i } -func SysRole() ISysRole { - if localSysRole == nil { - panic("implement not found for interface ISysRole, forgot register?") +func SysJob() ISysJob { + if localSysJob == nil { + panic("implement not found for interface ISysJob, forgot register?") } - return localSysRole + return localSysJob } -func RegisterSysRole(i ISysRole) { - localSysRole = i +func RegisterSysJob(i ISysJob) { + localSysJob = i } -func SysToken() ISysToken { - if localSysToken == nil { - panic("implement not found for interface ISysToken, forgot register?") +func SysMenuApi() ISysMenuApi { + if localSysMenuApi == nil { + panic("implement not found for interface ISysMenuApi, forgot register?") } - return localSysToken + return localSysMenuApi } -func RegisterSysToken(i ISysToken) { - localSysToken = i +func RegisterSysMenuApi(i ISysMenuApi) { + localSysMenuApi = i } -func SysUser() ISysUser { - if localSysUser == nil { - panic("implement not found for interface ISysUser, forgot register?") +func SysMenuColumn() ISysMenuColumn { + if localSysMenuColumn == nil { + panic("implement not found for interface ISysMenuColumn, forgot register?") } - return localSysUser + return localSysMenuColumn } -func RegisterSysUser(i ISysUser) { - localSysUser = i +func RegisterSysMenuColumn(i ISysMenuColumn) { + localSysMenuColumn = i } -func SysAuthorize() ISysAuthorize { - if localSysAuthorize == nil { - panic("implement not found for interface ISysAuthorize, forgot register?") +func SysOrganization() ISysOrganization { + if localSysOrganization == nil { + panic("implement not found for interface ISysOrganization, forgot register?") } - return localSysAuthorize + return localSysOrganization } -func RegisterSysAuthorize(i ISysAuthorize) { - localSysAuthorize = i +func RegisterSysOrganization(i ISysOrganization) { + localSysOrganization = i } -func SysLoginLog() ISysLoginLog { - if localSysLoginLog == nil { - panic("implement not found for interface ISysLoginLog, forgot register?") +func SystemPluginsConfig() ISystemPluginsConfig { + if localSystemPluginsConfig == nil { + panic("implement not found for interface ISystemPluginsConfig, forgot register?") } - return localSysLoginLog + return localSystemPluginsConfig } -func RegisterSysLoginLog(i ISysLoginLog) { - localSysLoginLog = i +func RegisterSystemPluginsConfig(i ISystemPluginsConfig) { + localSystemPluginsConfig = i } -func SysMenuApi() ISysMenuApi { - if localSysMenuApi == nil { - panic("implement not found for interface ISysMenuApi, forgot register?") +func SysPost() ISysPost { + if localSysPost == nil { + panic("implement not found for interface ISysPost, forgot register?") } - return localSysMenuApi + return localSysPost } -func RegisterSysMenuApi(i ISysMenuApi) { - localSysMenuApi = i +func RegisterSysPost(i ISysPost) { + localSysPost = i } -func SysOrganization() ISysOrganization { - if localSysOrganization == nil { - panic("implement not found for interface ISysOrganization, forgot register?") +func SysRole() ISysRole { + if localSysRole == nil { + panic("implement not found for interface ISysRole, forgot register?") } - return localSysOrganization + return localSysRole } -func RegisterSysOrganization(i ISysOrganization) { - localSysOrganization = i +func RegisterSysRole(i ISysRole) { + localSysRole = i } func SysUserOnline() ISysUserOnline { @@ -406,15 +616,37 @@ func RegisterSysUserOnline(i ISysUserOnline) { localSysUserOnline = i } -func SysUserPost() ISysUserPost { - if localSysUserPost == nil { - panic("implement not found for interface ISysUserPost, forgot register?") +func Captcha() ICaptcha { + if localCaptcha == nil { + panic("implement not found for interface ICaptcha, forgot register?") } - return localSysUserPost + return localCaptcha } -func RegisterSysUserPost(i ISysUserPost) { - localSysUserPost = i +func RegisterCaptcha(i ICaptcha) { + localCaptcha = i +} + +func SysAuthorize() ISysAuthorize { + if localSysAuthorize == nil { + panic("implement not found for interface ISysAuthorize, forgot register?") + } + return localSysAuthorize +} + +func RegisterSysAuthorize(i ISysAuthorize) { + localSysAuthorize = i +} + +func SysCertificate() ISysCertificate { + if localSysCertificate == nil { + panic("implement not found for interface ISysCertificate, forgot register?") + } + return localSysCertificate +} + +func RegisterSysCertificate(i ISysCertificate) { + localSysCertificate = i } func SysMenu() ISysMenu { @@ -439,17 +671,6 @@ func RegisterSysMenuButton(i ISysMenuButton) { localSysMenuButton = i } -func SysMenuColumn() ISysMenuColumn { - if localSysMenuColumn == nil { - panic("implement not found for interface ISysMenuColumn, forgot register?") - } - return localSysMenuColumn -} - -func RegisterSysMenuColumn(i ISysMenuColumn) { - localSysMenuColumn = i -} - func SysRoleDept() ISysRoleDept { if localSysRoleDept == nil { panic("implement not found for interface ISysRoleDept, forgot register?") @@ -461,15 +682,15 @@ func RegisterSysRoleDept(i ISysRoleDept) { localSysRoleDept = i } -func SystemPluginsConfig() ISystemPluginsConfig { - if localSystemPluginsConfig == nil { - panic("implement not found for interface ISystemPluginsConfig, forgot register?") +func SysUser() ISysUser { + if localSysUser == nil { + panic("implement not found for interface ISysUser, forgot register?") } - return localSystemPluginsConfig + return localSysUser } -func RegisterSystemPluginsConfig(i ISystemPluginsConfig) { - localSystemPluginsConfig = i +func RegisterSysUser(i ISysUser) { + localSysUser = i } func SysUserRole() ISysUserRole { @@ -483,46 +704,57 @@ func RegisterSysUserRole(i ISysUserRole) { localSysUserRole = i } -func Captcha() ICaptcha { - if localCaptcha == nil { - panic("implement not found for interface ICaptcha, forgot register?") +func SysDept() ISysDept { + if localSysDept == nil { + panic("implement not found for interface ISysDept, forgot register?") } - return localCaptcha + return localSysDept } -func RegisterCaptcha(i ICaptcha) { - localCaptcha = i +func RegisterSysDept(i ISysDept) { + localSysDept = i } -func Login() ILogin { - if localLogin == nil { - panic("implement not found for interface ILogin, forgot register?") +func SysMessage() ISysMessage { + if localSysMessage == nil { + panic("implement not found for interface ISysMessage, forgot register?") } - return localLogin + return localSysMessage } -func RegisterLogin(i ILogin) { - localLogin = i +func RegisterSysMessage(i ISysMessage) { + localSysMessage = i } -func SysJob() ISysJob { - if localSysJob == nil { - panic("implement not found for interface ISysJob, forgot register?") +func SysNotifications() ISysNotifications { + if localSysNotifications == nil { + panic("implement not found for interface ISysNotifications, forgot register?") } - return localSysJob + return localSysNotifications } -func RegisterSysJob(i ISysJob) { - localSysJob = i +func RegisterSysNotifications(i ISysNotifications) { + localSysNotifications = i } -func SysOperLog() ISysOperLog { - if localSysOperLog == nil { - panic("implement not found for interface ISysOperLog, forgot register?") +func SysPlugins() ISysPlugins { + if localSysPlugins == nil { + panic("implement not found for interface ISysPlugins, forgot register?") } - return localSysOperLog + return localSysPlugins } -func RegisterSysOperLog(i ISysOperLog) { - localSysOperLog = i +func RegisterSysPlugins(i ISysPlugins) { + localSysPlugins = i +} + +func SysUserPost() ISysUserPost { + if localSysUserPost == nil { + panic("implement not found for interface ISysUserPost, forgot register?") + } + return localSysUserPost +} + +func RegisterSysUserPost(i ISysUserPost) { + localSysUserPost = i } diff --git a/internal/service/tdengine.go b/internal/service/tdengine.go index c3754b7..4e6ad93 100644 --- a/internal/service/tdengine.go +++ b/internal/service/tdengine.go @@ -1,5 +1,5 @@ // ================================================================================ -// Code generated by GoFrame CLI tool. DO NOT EDIT. +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. // You can delete these comments if you wish manually maintain this interface file. // ================================================================================ @@ -8,49 +8,66 @@ package service import ( "context" "database/sql" - "github.com/sagoo-cloud/sagooiot/internal/model" + "sagooiot/internal/model" - "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" ) type ( ITdEngine interface { - GetConn(ctx context.Context, dbName string) (db *sql.DB, err error) - GetTdEngineAllDb(ctx context.Context) (data []string, err error) - GetListTableByDatabases(ctx context.Context, dbName string) (data []*model.TDEngineTablesList, err error) - GetTdEngineTableInfoByTable(ctx context.Context, dbName string, tableName string) (data []*model.TDEngineTableInfo, err error) - GetTdEngineTableDataByTable(ctx context.Context, dbName string, tableName string) (data *model.TableDataInfo, err error) - GetOne(ctx context.Context, sql string, args ...any) (rs gdb.Record, err error) - GetAll(ctx context.Context, sql string, args ...any) (rs gdb.Result, err error) + // GetConn 获取数据库连接 + GetConn(ctx context.Context, dbName string) (*sql.DB, error) + // Time REST连接时区处理 Time(v *g.Var) (rs *g.Var) + // ClearLogByDays 删除指定天数的设备日志数据 + ClearLogByDays(ctx context.Context, days int) (err error) } ITdLogTable interface { + // 添加超级表 CreateStable(ctx context.Context) (err error) + // 写入数据 Insert(ctx context.Context, log *model.TdLogAddInput) (err error) + // 清理过期数据 Clear(ctx context.Context) (err error) + // 超级表查询,多条数据 GetAll(ctx context.Context, sql string, args ...any) (list []model.TdLog, err error) } ITSLTable interface { - Insert(ctx context.Context, deviceKey string, data map[string]any) (err error) + // Insert 数据入库 + Insert(ctx context.Context, deviceKey string, data model.ReportPropertyData, subKey ...string) (err error) + // 添加超级表 CreateStable(ctx context.Context, tsl *model.TSL) (err error) + // 添加子表 CreateTable(ctx context.Context, stable, table string) (err error) - DropStable(ctx context.Context, table string) (err error) + // 删除超级表 + DropStable(ctx context.Context, stable string) (err error) + // 删除子表 DropTable(ctx context.Context, table string) (err error) + // 创建数据库 CreateDatabase(ctx context.Context) (err error) + // CheckStable 查询超级表是否存在, true=存在 + CheckStable(ctx context.Context, stable string) (b bool, err error) + // CheckTable 查询子表是否存在, true=存在 + CheckTable(ctx context.Context, table string) (b bool, err error) + // AddDatabaseField 添加数据库字段 AddDatabaseField(ctx context.Context, tableName, fieldName string, dataType string, len int) (err error) + // DelDatabaseField 删除数据库字段 DelDatabaseField(ctx context.Context, tableName, fieldName string) (err error) + // ModifyDatabaseField 修改数据库指定字段长度 ModifyDatabaseField(ctx context.Context, tableName, fieldName string, dataType string, len int) (err error) + // AddTag 添加标签 AddTag(ctx context.Context, tableName, tagName string, dataType string, len int) (err error) + // DelTag 删除标签 DelTag(ctx context.Context, tableName, tagName string) (err error) + // ModifyTag 修改标签 ModifyTag(ctx context.Context, tableName, tagName string, dataType string, len int) (err error) } ) var ( + localTdEngine ITdEngine localTdLogTable ITdLogTable localTSLTable ITSLTable - localTdEngine ITdEngine ) func TdEngine() ITdEngine { diff --git a/internal/sse/base.go b/internal/sse/base.go new file mode 100644 index 0000000..ca0fdcb --- /dev/null +++ b/internal/sse/base.go @@ -0,0 +1,18 @@ +package sse + +import ( + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/sse/sysenv" +) + +func Init() { + sysenv.SysStartTime = gtime.Now() //初始化开始时间 + + for _, f := range []func(){runRedisInfo, runSysEnv} { + go func(f func()) { + for { + f() + } + }(f) + } +} diff --git a/internal/sse/mysql_html_test.html b/internal/sse/mysql_html_test.html new file mode 100644 index 0000000..3aba252 --- /dev/null +++ b/internal/sse/mysql_html_test.html @@ -0,0 +1,37 @@ + + + + + +

IOT Info:

+
    + + + + diff --git a/internal/sse/mysql_info_enevt.go b/internal/sse/mysql_info_enevt.go new file mode 100644 index 0000000..a389f25 --- /dev/null +++ b/internal/sse/mysql_info_enevt.go @@ -0,0 +1,37 @@ +package sse + +import ( + "github.com/gogf/gf/v2/net/ghttp" + sseserver "github.com/xinjiayu/sse" + "sagooiot/internal/sse/sysenv" +) + +var mysqlInfoEvent = sseserver.NewServer() + +func runMysqlInfo() { + //所有mysql版本信息 + versionMsg := sseserver.SSEMessage{} + versionMsg.Event = "version" + versionMsg.Data = sysenv.GetMysqlVersionInfo() + versionMsg.Namespace = "/mysqlinfo/version" + mysqlInfoEvent.Broadcast <- versionMsg + + //所有mysql的status信息 + statusMsg := sseserver.SSEMessage{} + statusMsg.Event = "status" + statusMsg.Data = sysenv.GetMysqlStatusInfo() + statusMsg.Namespace = "/mysqlinfo/status" + mysqlInfoEvent.Broadcast <- statusMsg + + //所有mysql统计量信息 + variablesMsg := sseserver.SSEMessage{} + variablesMsg.Event = "variables" + variablesMsg.Data = sysenv.GetMysqlVariablesInfo() + variablesMsg.Namespace = "/mysqlinfo/variables" + mysqlInfoEvent.Broadcast <- variablesMsg +} + +func MysqlInfoMessageEvent(r *ghttp.Request) { + mysqlInfoEvent.ServeHTTP(r.Response.RawWriter(), r.Request) + +} diff --git a/internal/sse/redis_html_test.html b/internal/sse/redis_html_test.html new file mode 100644 index 0000000..59d4bd0 --- /dev/null +++ b/internal/sse/redis_html_test.html @@ -0,0 +1,83 @@ + + + + + +

    IOT Info:

    +
      + + + + diff --git a/internal/sse/redis_info_enent.go b/internal/sse/redis_info_enent.go new file mode 100644 index 0000000..3a56e5c --- /dev/null +++ b/internal/sse/redis_info_enent.go @@ -0,0 +1,74 @@ +package sse + +import ( + "github.com/gogf/gf/v2/net/ghttp" + sseserver "github.com/xinjiayu/sse" + "sagooiot/internal/sse/sysenv" + "time" +) + +var redisInfoEvent = sseserver.NewServer() + +func runRedisInfo() { + redisInfoData := sysenv.GetRedisInfo() + + cpuMsg := sseserver.SSEMessage{} + cpuMsg.Event = "cpu" + cpuMsg.Data = redisInfoData["CPU"] + cpuMsg.Namespace = "/redisinfo/cpu" + redisInfoEvent.Broadcast <- cpuMsg + + redisMsg := sseserver.SSEMessage{} + redisMsg.Event = "server" + redisMsg.Data = redisInfoData["Server"] + redisMsg.Namespace = "/redisinfo/server" + redisInfoEvent.Broadcast <- redisMsg + + clientsMsg := sseserver.SSEMessage{} + clientsMsg.Event = "clients" + clientsMsg.Data = redisInfoData["Clients"] + clientsMsg.Namespace = "/redisinfo/clients" + redisInfoEvent.Broadcast <- clientsMsg + + errorstatsMsg := sseserver.SSEMessage{} + errorstatsMsg.Event = "errorstats" + errorstatsMsg.Data = redisInfoData["Errorstats"] + errorstatsMsg.Namespace = "/redisinfo/errorstats" + redisInfoEvent.Broadcast <- errorstatsMsg + + keyspaceMsg := sseserver.SSEMessage{} + keyspaceMsg.Event = "keyspace" + keyspaceMsg.Data = redisInfoData["Keyspace"] + keyspaceMsg.Namespace = "/redisinfo/keyspace" + redisInfoEvent.Broadcast <- keyspaceMsg + + clusterMsg := sseserver.SSEMessage{} + clusterMsg.Event = "cluster" + clusterMsg.Data = redisInfoData["Cluster"] + clusterMsg.Namespace = "/redisinfo/cluster" + redisInfoEvent.Broadcast <- clusterMsg + + memoryMsg := sseserver.SSEMessage{} + memoryMsg.Event = "memory" + memoryMsg.Data = redisInfoData["Memory"] + memoryMsg.Namespace = "/redisinfo/memory" + redisInfoEvent.Broadcast <- memoryMsg + + statsMsg := sseserver.SSEMessage{} + statsMsg.Event = "stats" + statsMsg.Data = redisInfoData["Stats"] + statsMsg.Namespace = "/redisinfo/stats" + redisInfoEvent.Broadcast <- statsMsg + + replicationMsg := sseserver.SSEMessage{} + replicationMsg.Event = "replication" + replicationMsg.Data = redisInfoData["Replication"] + replicationMsg.Namespace = "/redisinfo/replication" + redisInfoEvent.Broadcast <- replicationMsg + + time.Sleep(time.Duration(1) * time.Second) +} + +func RedisInfoMessageEvent(r *ghttp.Request) { + redisInfoEvent.ServeHTTP(r.Response.RawWriter(), r.Request) +} diff --git a/internal/sse/sys_message_enevt.go b/internal/sse/sys_message_enevt.go new file mode 100644 index 0000000..4c34cd0 --- /dev/null +++ b/internal/sse/sys_message_enevt.go @@ -0,0 +1,36 @@ +package sse + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/util/gconv" + "github.com/xinjiayu/sse" + "sagooiot/internal/sse/sysenv" + "time" +) + +func SysMessageEntvt(r *ghttp.Request) { + sseServer := sseserver.NewServer() + userId := gconv.Int(r.GetQuery("userId")) + isCLose := make(chan bool) + go func() { + for { + select { + case <-isCLose: + return + default: + data := sysenv.GetUnReadMessageLast(userId) + + if data != nil && len(data) > 0 { + sysMessage := sseserver.SSEMessage{} + sysMessage.Event = "lastUnRead" + sysMessage.Data = data + sysMessage.Namespace = "/sysMessage/lastUnRead" + sseServer.Broadcast <- sysMessage + } + time.Sleep(time.Duration(5) * time.Second) + } + } + }() + sseServer.ServeHTTP(r.Response.RawWriter(), r.Request) + isCLose <- true +} diff --git a/internal/sse/sys_message_test.html b/internal/sse/sys_message_test.html new file mode 100644 index 0000000..9fbc18b --- /dev/null +++ b/internal/sse/sys_message_test.html @@ -0,0 +1,28 @@ + + + + + +

      System Info:

      +
        + + + + diff --git a/internal/sse/sysenv/messageinfo.go b/internal/sse/sysenv/messageinfo.go new file mode 100644 index 0000000..a815b36 --- /dev/null +++ b/internal/sse/sysenv/messageinfo.go @@ -0,0 +1,19 @@ +package sysenv + +import ( + "context" + "encoding/json" + "sagooiot/internal/service" +) + +// GetUnReadMessageLast 获取用户最后一条未读消息 +func GetUnReadMessageLast(userId int) (data []byte) { + tmpData, err := service.SysMessage().GetUnReadMessageLast(context.TODO(), userId) + if err != nil { + return + } + if tmpData != nil && len(tmpData) > 0 { + data, _ = json.Marshal(tmpData) + } + return +} diff --git a/internal/sse/sysenv/mysqlinfo.go b/internal/sse/sysenv/mysqlinfo.go new file mode 100644 index 0000000..08749fd --- /dev/null +++ b/internal/sse/sysenv/mysqlinfo.go @@ -0,0 +1,61 @@ +package sysenv + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "log" +) + +// GetMysqlStatusInfo 获取MySql的运行状态信息 +func GetMysqlStatusInfo() (data []byte) { + db := g.DB() + var queryStatusSQL = "show global status" + list, err := db.GetAll(context.TODO(), queryStatusSQL) + if err != nil { + log.Println(err.Error()) + } + var tmpData = make(map[string]interface{}) + for _, v := range list { + tmpData[v["Variable_name"].String()] = v["Value"] + } + + data, _ = json.Marshal(tmpData) + return +} + +// GetMysqlVersionInfo 获取版本信息 +func GetMysqlVersionInfo() (data []byte) { + db := g.DB() + var queryVersionSQL = "select version() as version limit 1" + list, err := db.GetAll(context.TODO(), queryVersionSQL) + if err != nil { + log.Println(err.Error()) + } + var tmpData = make(map[string]interface{}) + for _, v := range list { + //log.Println(v) + tmpData = v.Map() + //tmpData[v["Variable_name"].String()] = v["Value"] + } + + data, _ = json.Marshal(tmpData) + return +} + +// GetMysqlVariablesInfo 获取variables的运行信息 +func GetMysqlVariablesInfo() (data []byte) { + db := g.DB() + var queryVariablesSQL = "show global variables" + list, err := db.GetAll(context.TODO(), queryVariablesSQL) + if err != nil { + log.Println(err.Error()) + } + var tmpData = make(map[string]interface{}) + for _, v := range list { + tmpData[v["Variable_name"].String()] = v["Value"] + } + + data, _ = json.Marshal(tmpData) + return +} diff --git a/internal/sse/sysenv/mysqlinfo_test.go b/internal/sse/sysenv/mysqlinfo_test.go new file mode 100644 index 0000000..7378f31 --- /dev/null +++ b/internal/sse/sysenv/mysqlinfo_test.go @@ -0,0 +1,28 @@ +package sysenv + +import ( + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" + + "testing" +) + +func TestGetMysqlStatusInfo(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := GetMysqlStatusInfo() + g.Dump(a) + }) +} +func TestGetMysqlVersionInfo(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := GetMysqlVersionInfo() + g.Dump(a) + }) +} +func TestGetMysqlVariablesInfo(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := GetMysqlVariablesInfo() + g.Dump(a) + }) +} diff --git a/internal/sse/sysenv/redisinfo.go b/internal/sse/sysenv/redisinfo.go new file mode 100644 index 0000000..14cb857 --- /dev/null +++ b/internal/sse/sysenv/redisinfo.go @@ -0,0 +1,67 @@ +package sysenv + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/database/gredis" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gctx" + "strings" +) + +// GetRedisInfo 获取Redis运行信息 +func GetRedisInfo() (data map[string][]byte) { + ctx := gctx.GetInitCtx() + conn, _ := g.Redis().Conn(ctx) + defer func(conn gredis.Conn, ctx context.Context) { + if err := conn.Close(ctx); err != nil { + fmt.Println(err) + } + }(conn, ctx) + info, _ := conn.Do(ctx, "INFO") + + var result = make(map[string][]map[string]interface{}) + scanner := bufio.NewScanner(strings.NewReader(info.String())) + var key string + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "#") { + key = strings.TrimSpace(strings.Split(line, "#")[1]) + result[key] = make([]map[string]interface{}, 0) + } else if len(line) != 0 { + kv := strings.Split(line, ":") + m := make(map[string]interface{}) + + //判断指标值是否有多个 + if strings.Contains(kv[1], ",") { + sunValueList := strings.Split(kv[1], ",") + sunValue := make(map[string]interface{}) + for _, s := range sunValueList { + skv := strings.Split(s, "=") + sunValue[skv[0]] = skv[1] + } + m[kv[0]] = sunValue + } else { + m[kv[0]] = kv[1] + + } + + result[key] = append(result[key], m) + } + } + + var res = make(map[string][]byte) + for k, vList := range result { + var value = make(map[string]interface{}) + for _, v := range vList { + for k1, v1 := range v { + value[k1] = v1 + } + } + res[k], _ = json.Marshal(value) + } + + return res +} diff --git a/internal/sse/sysenv/redisinfo_test.go b/internal/sse/sysenv/redisinfo_test.go new file mode 100644 index 0000000..0e2fefc --- /dev/null +++ b/internal/sse/sysenv/redisinfo_test.go @@ -0,0 +1,14 @@ +package sysenv + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" + "testing" +) + +func TestGetRedisInfo(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := GetRedisInfo() + g.Dump(a) + }) +} diff --git a/internal/sse/sysenv/runinfo.go b/internal/sse/sysenv/runinfo.go new file mode 100644 index 0000000..3c02647 --- /dev/null +++ b/internal/sse/sysenv/runinfo.go @@ -0,0 +1,45 @@ +package sysenv + +import ( + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "os" + "runtime" + "sagooiot/pkg/utility/utils" +) + +var ( + SysStartTime *gtime.Time + LocalIP string //本地IP + PublicIP string //公网IP + GoDiskSize string //Go程序占磁盘空间 +) + +// GetGoRunInfo 获取运行信息 +func GetGoRunInfo() (data []byte) { + var ( + tmpData = g.Map{} + SysRunDir, _ = os.Getwd() + gm runtime.MemStats + ) + + runtime.ReadMemStats(&gm) + // GO运行信息 + tmpData = g.Map{ + "goName": "Golang", + "goOs": runtime.GOOS, //操作系统 + "arch": runtime.GOARCH, //系统架构 + "goVersion": runtime.Version(), //GO版本 + "startTime": SysStartTime, //系统开始时间 + "runTime": gtime.Now().Timestamp() - SysStartTime.Timestamp(), //运行时长 + "rootPath": runtime.GOROOT(), + "pwd": SysRunDir, + "goroutine": runtime.NumGoroutine(), + "goMem": utils.FileSize(int64(gm.Sys)), //运行内存 + "goSize": GoDiskSize, //磁盘占用 + } + + data, _ = json.Marshal(tmpData) + return +} diff --git a/utility/notifier/sysenv/sysenv.go b/internal/sse/sysenv/sysenv.go similarity index 80% rename from utility/notifier/sysenv/sysenv.go rename to internal/sse/sysenv/sysenv.go index 3521f63..cad363d 100644 --- a/utility/notifier/sysenv/sysenv.go +++ b/internal/sse/sysenv/sysenv.go @@ -14,18 +14,21 @@ import ( "time" ) -//GetHostInfo 获取主机信息 +// GetHostInfo 获取主机信息 func GetHostInfo() (data []byte) { hostInfo, _ := host.Info() timestamp, _ := host.BootTime() t := time.Unix(int64(timestamp), 0) tmpData := gconv.Map(hostInfo) tmpData["bootTime"] = t + + tmpData["intranet_ip"] = LocalIP + tmpData["public_ip"] = PublicIP data = gconv.Bytes(tmpData) return } -//GetSysLoad 获取系统负载信息 +// GetSysLoad 获取系统负载信息 func GetSysLoad() (data []byte) { loadInfo, _ := load.Avg() data = gconv.Bytes(g.Map{ @@ -40,9 +43,10 @@ type CpuInfo struct { Number int //cup个数 Cores int32 //核数 UsedPercent []float64 //cpu使用率 + ModelName string } -//GetCpuInfo 获取CPU信息 +// GetCpuInfo 获取CPU信息 func GetCpuInfo() (data []byte) { var CpuInfoData CpuInfo cpus, _ := cpu.Info() @@ -52,32 +56,27 @@ func GetCpuInfo() (data []byte) { CpuInfoData.Number = len(cpus) percent, _ := cpu.Percent(time.Second, false) //获取CPU使用率 CpuInfoData.UsedPercent = percent + CpuInfoData.ModelName = cpus[0].ModelName //CPU型号 data, _ = json.Marshal(CpuInfoData) return } -//GetMemInfo 获取内存信息 +// GetMemInfo 获取内存信息 func GetMemInfo() (data []byte) { hostInfo, _ := mem.VirtualMemory() tmpData := gconv.Map(hostInfo) var gomem runtime.MemStats runtime.ReadMemStats(&gomem) + if tmpData == nil { + tmpData = make(map[string]interface{}) + } tmpData["goUsed"] = gomem.Sys - data, _ = json.Marshal(tmpData) return } -//GetDiskInfo 获取磁盘信息 +// GetDiskInfo 获取磁盘信息 func GetDiskInfo() (data []byte) { - //diskPart, _ := disk.Partitions(false) - //for _, dp := range diskPart { - // fmt.Println(dp) - // diskUsed, _ := disk.Usage(dp.Mountpoint) - // fmt.Printf("分区总大小: %d MB \n", diskUsed.Total/1024/1024) - // fmt.Printf("分区使用率: %.3f %% \n", diskUsed.UsedPercent) - // fmt.Printf("分区inode使用率: %.3f %% \n", diskUsed.InodesUsedPercent) - //} diskUsed, _ := disk.Usage("/") data = gconv.Bytes(diskUsed) return @@ -92,7 +91,7 @@ type NetWorkInfo struct { SentSpeed uint64 } -//GetNetStatusInfo 获取网络信息 +// GetNetStatusInfo 获取网络信息 func GetNetStatusInfo() (data []byte) { IOCountersStat, _ := net.IOCounters(true) netWorkInfo := make([]NetWorkInfo, len(IOCountersStat)) diff --git a/internal/sse/sysenv/tdinfo.go b/internal/sse/sysenv/tdinfo.go new file mode 100644 index 0000000..eddd14b --- /dev/null +++ b/internal/sse/sysenv/tdinfo.go @@ -0,0 +1,6 @@ +package sysenv + +func GetTDengineInfo() (data map[string][]byte) { + + return +} diff --git a/internal/sse/sysenv_enevt.go b/internal/sse/sysenv_enevt.go new file mode 100644 index 0000000..2687d83 --- /dev/null +++ b/internal/sse/sysenv_enevt.go @@ -0,0 +1,60 @@ +package sse + +import ( + "github.com/gogf/gf/v2/net/ghttp" + sseserver "github.com/xinjiayu/sse" + "sagooiot/internal/sse/sysenv" + "time" +) + +var sysEnvEvent = sseserver.NewServer() + +func runSysEnv() { + hostMsg := sseserver.SSEMessage{} + hostMsg.Event = "host" + hostMsg.Data = sysenv.GetHostInfo() + hostMsg.Namespace = "/sysenv/host" + sysEnvEvent.Broadcast <- hostMsg + + sysLoadMsg := sseserver.SSEMessage{} + sysLoadMsg.Event = "sysLoad" + sysLoadMsg.Data = sysenv.GetSysLoad() + sysLoadMsg.Namespace = "/sysenv/sysLoad" + sysEnvEvent.Broadcast <- sysLoadMsg + + cpuMsg := sseserver.SSEMessage{} + cpuMsg.Event = "cpu" + cpuMsg.Data = sysenv.GetCpuInfo() + cpuMsg.Namespace = "/sysenv/cpu" + sysEnvEvent.Broadcast <- cpuMsg + + memMsg := sseserver.SSEMessage{} + memMsg.Event = "mem" + memMsg.Data = sysenv.GetMemInfo() + memMsg.Namespace = "/sysenv/mem" + sysEnvEvent.Broadcast <- memMsg + + diskMsg := sseserver.SSEMessage{} + diskMsg.Event = "disk" + diskMsg.Data = sysenv.GetDiskInfo() + diskMsg.Namespace = "/sysenv/disk" + sysEnvEvent.Broadcast <- diskMsg + + netMsg := sseserver.SSEMessage{} + netMsg.Event = "net" + netMsg.Data = sysenv.GetNetStatusInfo() + netMsg.Namespace = "/sysenv/net" + sysEnvEvent.Broadcast <- netMsg + + goMsg := sseserver.SSEMessage{} + goMsg.Event = "go" + goMsg.Data = sysenv.GetGoRunInfo() + goMsg.Namespace = "/sysenv/go" + sysEnvEvent.Broadcast <- goMsg + + time.Sleep(time.Duration(1) * time.Second) +} + +func SysenvMessageEvent(r *ghttp.Request) { + sysEnvEvent.ServeHTTP(r.Response.RawWriter(), r.Request) +} diff --git a/utility/notifier/sysenv_html_test.html b/internal/sse/sysenv_html_test.html similarity index 89% rename from utility/notifier/sysenv_html_test.html rename to internal/sse/sysenv_html_test.html index f9e577a..5b3de57 100644 --- a/utility/notifier/sysenv_html_test.html +++ b/internal/sse/sysenv_html_test.html @@ -7,8 +7,8 @@

        System Info:

          - - - - - - -
          - - - \ No newline at end of file diff --git a/manifest/docker-compose/nginx/nginx.yaml b/manifest/docker-compose/nginx/nginx.yaml deleted file mode 100644 index 16a05c3..0000000 --- a/manifest/docker-compose/nginx/nginx.yaml +++ /dev/null @@ -1,13 +0,0 @@ -services: - nginx: - image: nginx:1.25.1 - restart: always - container_name: nginx - privileged: true - ports: - - 80:80 - - 443:443 - volumes: - - "./conf/nginx.conf:/etc/nginx/nginx.conf" - - "./html/:/usr/share/nginx/html/" - - "./logs/:/var/log/nginx/" diff --git a/manifest/docker-compose/redis/conf/redis.conf b/manifest/docker-compose/redis/conf/redis.conf deleted file mode 100644 index 64776f5..0000000 --- a/manifest/docker-compose/redis/conf/redis.conf +++ /dev/null @@ -1,2280 +0,0 @@ -# Redis configuration file example. -# -# Note that in order to read the configuration file, Redis must be -# started with the file path as first argument: -# -# ./redis-server /path/to/redis.conf - -# Note on units: when memory size is needed, it is possible to specify -# it in the usual form of 1k 5GB 4M and so forth: -# -# 1k => 1000 bytes -# 1kb => 1024 bytes -# 1m => 1000000 bytes -# 1mb => 1024*1024 bytes -# 1g => 1000000000 bytes -# 1gb => 1024*1024*1024 bytes -# -# units are case insensitive so 1GB 1Gb 1gB are all the same. - -################################## INCLUDES ################################### - -# Include one or more other config files here. This is useful if you -# have a standard template that goes to all Redis servers but also need -# to customize a few per-server settings. Include files can include -# other files, so use this wisely. -# -# Note that option "include" won't be rewritten by command "CONFIG REWRITE" -# from admin or Redis Sentinel. Since Redis always uses the last processed -# line as value of a configuration directive, you'd better put includes -# at the beginning of this file to avoid overwriting config change at runtime. -# -# If instead you are interested in using includes to override configuration -# options, it is better to use include as the last line. -# -# Included paths may contain wildcards. All files matching the wildcards will -# be included in alphabetical order. -# Note that if an include path contains a wildcards but no files match it when -# the server is started, the include statement will be ignored and no error will -# be emitted. It is safe, therefore, to include wildcard files from empty -# directories. -# -# include /path/to/local.conf -# include /path/to/other.conf -# include /path/to/fragments/*.conf -# - -################################## MODULES ##################################### - -# Load modules at startup. If the server is not able to load modules -# it will abort. It is possible to use multiple loadmodule directives. -# -# loadmodule /path/to/my_module.so -# loadmodule /path/to/other_module.so - -################################## NETWORK ##################################### - -# By default, if no "bind" configuration directive is specified, Redis listens -# for connections from all available network interfaces on the host machine. -# It is possible to listen to just one or multiple selected interfaces using -# the "bind" configuration directive, followed by one or more IP addresses. -# Each address can be prefixed by "-", which means that redis will not fail to -# start if the address is not available. Being not available only refers to -# addresses that does not correspond to any network interface. Addresses that -# are already in use will always fail, and unsupported protocols will always BE -# silently skipped. -# -# Examples: -# -# bind 192.168.1.100 10.0.0.1 # listens on two specific IPv4 addresses -# bind 127.0.0.1 ::1 # listens on loopback IPv4 and IPv6 -# bind * -::* # like the default, all available interfaces -# -# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the -# internet, binding to all the interfaces is dangerous and will expose the -# instance to everybody on the internet. So by default we uncomment the -# following bind directive, that will force Redis to listen only on the -# IPv4 and IPv6 (if available) loopback interface addresses (this means Redis -# will only be able to accept client connections from the same host that it is -# running on). -# -# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES -# COMMENT OUT THE FOLLOWING LINE. -# -# You will also need to set a password unless you explicitly disable protected -# mode. -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -#注释掉这部分,使redis可以外部访问 -# bind 127.0.0.1 -::1 - -# By default, outgoing connections (from replica to master, from Sentinel to -# instances, cluster bus, etc.) are not bound to a specific local address. In -# most cases, this means the operating system will handle that based on routing -# and the interface through which the connection goes out. -# -# Using bind-source-addr it is possible to configure a specific address to bind -# to, which may also affect how the connection gets routed. -# -# Example: -# -# bind-source-addr 10.0.0.1 - -# Protected mode is a layer of security protection, in order to avoid that -# Redis instances left open on the internet are accessed and exploited. -# -# When protected mode is on and the default user has no password, the server -# only accepts local connections from the IPv4 address (127.0.0.1), IPv6 address -# (::1) or Unix domain sockets. -# -# By default protected mode is enabled. You should disable it only if -# you are sure you want clients from other hosts to connect to Redis -# even if no authentication is configured. -#保护模式,默认yes -protected-mode no - -# Redis uses default hardened security configuration directives to reduce the -# attack surface on innocent users. Therefore, several sensitive configuration -# directives are immutable, and some potentially-dangerous commands are blocked. -# -# Configuration directives that control files that Redis writes to (e.g., 'dir' -# and 'dbfilename') and that aren't usually modified during runtime -# are protected by making them immutable. -# -# Commands that can increase the attack surface of Redis and that aren't usually -# called by users are blocked by default. -# -# These can be exposed to either all connections or just local ones by setting -# each of the configs listed below to either of these values: -# -# no - Block for any connection (remain immutable) -# yes - Allow for any connection (no protection) -# local - Allow only for local connections. Ones originating from the -# IPv4 address (127.0.0.1), IPv6 address (::1) or Unix domain sockets. -# -# enable-protected-configs no -# enable-debug-command no -# enable-module-command no - -# Accept connections on the specified port, default is 6379 (IANA #815344). -# If port 0 is specified Redis will not listen on a TCP socket. -#端口号 -port 6379 - -# TCP listen() backlog. -# -# In high requests-per-second environments you need a high backlog in order -# to avoid slow clients connection issues. Note that the Linux kernel -# will silently truncate it to the value of /proc/sys/net/core/somaxconn so -# make sure to raise both the value of somaxconn and tcp_max_syn_backlog -# in order to get the desired effect. -tcp-backlog 511 - -# Unix socket. -# -# Specify the path for the Unix socket that will be used to listen for -# incoming connections. There is no default, so Redis will not listen -# on a unix socket when not specified. -# -# unixsocket /run/redis.sock -# unixsocketperm 700 - -# Close the connection after a client is idle for N seconds (0 to disable) -timeout 0 - -# TCP keepalive. -# -# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence -# of communication. This is useful for two reasons: -# -# 1) Detect dead peers. -# 2) Force network equipment in the middle to consider the connection to be -# alive. -# -# On Linux, the specified value (in seconds) is the period used to send ACKs. -# Note that to close the connection the double of the time is needed. -# On other kernels the period depends on the kernel configuration. -# -# A reasonable value for this option is 300 seconds, which is the new -# Redis default starting with Redis 3.2.1. -tcp-keepalive 300 - -# Apply OS-specific mechanism to mark the listening socket with the specified -# ID, to support advanced routing and filtering capabilities. -# -# On Linux, the ID represents a connection mark. -# On FreeBSD, the ID represents a socket cookie ID. -# On OpenBSD, the ID represents a route table ID. -# -# The default value is 0, which implies no marking is required. -# socket-mark-id 0 - -################################# TLS/SSL ##################################### - -# By default, TLS/SSL is disabled. To enable it, the "tls-port" configuration -# directive can be used to define TLS-listening ports. To enable TLS on the -# default port, use: -# -# port 0 -# tls-port 6379 - -# Configure a X.509 certificate and private key to use for authenticating the -# server to connected clients, masters or cluster peers. These files should be -# PEM formatted. -# -# tls-cert-file redis.crt -# tls-key-file redis.key -# -# If the key file is encrypted using a passphrase, it can be included here -# as well. -# -# tls-key-file-pass secret - -# Normally Redis uses the same certificate for both server functions (accepting -# connections) and client functions (replicating from a master, establishing -# cluster bus connections, etc.). -# -# Sometimes certificates are issued with attributes that designate them as -# client-only or server-only certificates. In that case it may be desired to use -# different certificates for incoming (server) and outgoing (client) -# connections. To do that, use the following directives: -# -# tls-client-cert-file client.crt -# tls-client-key-file client.key -# -# If the key file is encrypted using a passphrase, it can be included here -# as well. -# -# tls-client-key-file-pass secret - -# Configure a DH parameters file to enable Diffie-Hellman (DH) key exchange, -# required by older versions of OpenSSL (<3.0). Newer versions do not require -# this configuration and recommend against it. -# -# tls-dh-params-file redis.dh - -# Configure a CA certificate(s) bundle or directory to authenticate TLS/SSL -# clients and peers. Redis requires an explicit configuration of at least one -# of these, and will not implicitly use the system wide configuration. -# -# tls-ca-cert-file ca.crt -# tls-ca-cert-dir /etc/ssl/certs - -# By default, clients (including replica servers) on a TLS port are required -# to authenticate using valid client side certificates. -# -# If "no" is specified, client certificates are not required and not accepted. -# If "optional" is specified, client certificates are accepted and must be -# valid if provided, but are not required. -# -# tls-auth-clients no -# tls-auth-clients optional - -# By default, a Redis replica does not attempt to establish a TLS connection -# with its master. -# -# Use the following directive to enable TLS on replication links. -# -# tls-replication yes - -# By default, the Redis Cluster bus uses a plain TCP connection. To enable -# TLS for the bus protocol, use the following directive: -# -# tls-cluster yes - -# By default, only TLSv1.2 and TLSv1.3 are enabled and it is highly recommended -# that older formally deprecated versions are kept disabled to reduce the attack surface. -# You can explicitly specify TLS versions to support. -# Allowed values are case insensitive and include "TLSv1", "TLSv1.1", "TLSv1.2", -# "TLSv1.3" (OpenSSL >= 1.1.1) or any combination. -# To enable only TLSv1.2 and TLSv1.3, use: -# -# tls-protocols "TLSv1.2 TLSv1.3" - -# Configure allowed ciphers. See the ciphers(1ssl) manpage for more information -# about the syntax of this string. -# -# Note: this configuration applies only to <= TLSv1.2. -# -# tls-ciphers DEFAULT:!MEDIUM - -# Configure allowed TLSv1.3 ciphersuites. See the ciphers(1ssl) manpage for more -# information about the syntax of this string, and specifically for TLSv1.3 -# ciphersuites. -# -# tls-ciphersuites TLS_CHACHA20_POLY1305_SHA256 - -# When choosing a cipher, use the server's preference instead of the client -# preference. By default, the server follows the client's preference. -# -# tls-prefer-server-ciphers yes - -# By default, TLS session caching is enabled to allow faster and less expensive -# reconnections by clients that support it. Use the following directive to disable -# caching. -# -# tls-session-caching no - -# Change the default number of TLS sessions cached. A zero value sets the cache -# to unlimited size. The default size is 20480. -# -# tls-session-cache-size 5000 - -# Change the default timeout of cached TLS sessions. The default timeout is 300 -# seconds. -# -# tls-session-cache-timeout 60 - -################################# GENERAL ##################################### - -# By default Redis does not run as a daemon. Use 'yes' if you need it. -# Note that Redis will write a pid file in /var/run/redis.pid when daemonized. -# When Redis is supervised by upstart or systemd, this parameter has no impact. -#用守护线程的方式启动,关闭 -daemonize no - -# If you run Redis from upstart or systemd, Redis can interact with your -# supervision tree. Options: -# supervised no - no supervision interaction -# supervised upstart - signal upstart by putting Redis into SIGSTOP mode -# requires "expect stop" in your upstart job config -# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET -# on startup, and updating Redis status on a regular -# basis. -# supervised auto - detect upstart or systemd method based on -# UPSTART_JOB or NOTIFY_SOCKET environment variables -# Note: these supervision methods only signal "process is ready." -# They do not enable continuous pings back to your supervisor. -# -# The default is "no". To run under upstart/systemd, you can simply uncomment -# the line below: -# -# supervised auto - -# If a pid file is specified, Redis writes it where specified at startup -# and removes it at exit. -# -# When the server runs non daemonized, no pid file is created if none is -# specified in the configuration. When the server is daemonized, the pid file -# is used even if not specified, defaulting to "/var/run/redis.pid". -# -# Creating a pid file is best effort: if Redis is not able to create it -# nothing bad happens, the server will start and run normally. -# -# Note that on modern Linux systems "/run/redis.pid" is more conforming -# and should be used instead. -pidfile /var/run/redis_6380.pid - -# Specify the server verbosity level. -# This can be one of: -# debug (a lot of information, useful for development/testing) -# verbose (many rarely useful info, but not a mess like the debug level) -# notice (moderately verbose, what you want in production probably) -# warning (only very important / critical messages are logged) -loglevel notice - -# Specify the log file name. Also the empty string can be used to force -# Redis to log on the standard output. Note that if you use standard -# output for logging but daemonize, logs will be sent to /dev/null -logfile "" - -# To enable logging to the system logger, just set 'syslog-enabled' to yes, -# and optionally update the other syslog parameters to suit your needs. -# syslog-enabled no - -# Specify the syslog identity. -# syslog-ident redis - -# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. -# syslog-facility local0 - -# To disable the built in crash log, which will possibly produce cleaner core -# dumps when they are needed, uncomment the following: -# -# crash-log-enabled no - -# To disable the fast memory check that's run as part of the crash log, which -# will possibly let redis terminate sooner, uncomment the following: -# -# crash-memcheck-enabled no - -# Set the number of databases. The default database is DB 0, you can select -# a different one on a per-connection basis using SELECT where -# dbid is a number between 0 and 'databases'-1 -databases 16 - -# By default Redis shows an ASCII art logo only when started to log to the -# standard output and if the standard output is a TTY and syslog logging is -# disabled. Basically this means that normally a logo is displayed only in -# interactive sessions. -# -# However it is possible to force the pre-4.0 behavior and always show a -# ASCII art logo in startup logs by setting the following option to yes. -always-show-logo no - -# By default, Redis modifies the process title (as seen in 'top' and 'ps') to -# provide some runtime information. It is possible to disable this and leave -# the process name as executed by setting the following to no. -set-proc-title yes - -# When changing the process title, Redis uses the following template to construct -# the modified title. -# -# Template variables are specified in curly brackets. The following variables are -# supported: -# -# {title} Name of process as executed if parent, or type of child process. -# {listen-addr} Bind address or '*' followed by TCP or TLS port listening on, or -# Unix socket if only that's available. -# {server-mode} Special mode, i.e. "[sentinel]" or "[cluster]". -# {port} TCP port listening on, or 0. -# {tls-port} TLS port listening on, or 0. -# {unixsocket} Unix domain socket listening on, or "". -# {config-file} Name of configuration file used. -# -proc-title-template "{title} {listen-addr} {server-mode}" - -################################ SNAPSHOTTING ################################ - -# Save the DB to disk. -# -# save [ ...] -# -# Redis will save the DB if the given number of seconds elapsed and it -# surpassed the given number of write operations against the DB. -# -# Snapshotting can be completely disabled with a single empty string argument -# as in following example: -# -# save "" -# -# Unless specified otherwise, by default Redis will save the DB: -# * After 3600 seconds (an hour) if at least 1 change was performed -# * After 300 seconds (5 minutes) if at least 100 changes were performed -# * After 60 seconds if at least 10000 changes were performed -# -# You can set these explicitly by uncommenting the following line. -# -# save 3600 1 300 100 60 10000 - -# By default Redis will stop accepting writes if RDB snapshots are enabled -# (at least one save point) and the latest background save failed. -# This will make the user aware (in a hard way) that data is not persisting -# on disk properly, otherwise chances are that no one will notice and some -# disaster will happen. -# -# If the background saving process will start working again Redis will -# automatically allow writes again. -# -# However if you have setup your proper monitoring of the Redis server -# and persistence, you may want to disable this feature so that Redis will -# continue to work as usual even if there are problems with disk, -# permissions, and so forth. -stop-writes-on-bgsave-error yes - -# Compress string objects using LZF when dump .rdb databases? -# By default compression is enabled as it's almost always a win. -# If you want to save some CPU in the saving child set it to 'no' but -# the dataset will likely be bigger if you have compressible values or keys. -rdbcompression yes - -# Since version 5 of RDB a CRC64 checksum is placed at the end of the file. -# This makes the format more resistant to corruption but there is a performance -# hit to pay (around 10%) when saving and loading RDB files, so you can disable it -# for maximum performances. -# -# RDB files created with checksum disabled have a checksum of zero that will -# tell the loading code to skip the check. -rdbchecksum yes - -# Enables or disables full sanitization checks for ziplist and listpack etc when -# loading an RDB or RESTORE payload. This reduces the chances of a assertion or -# crash later on while processing commands. -# Options: -# no - Never perform full sanitization -# yes - Always perform full sanitization -# clients - Perform full sanitization only for user connections. -# Excludes: RDB files, RESTORE commands received from the master -# connection, and client connections which have the -# skip-sanitize-payload ACL flag. -# The default should be 'clients' but since it currently affects cluster -# resharding via MIGRATE, it is temporarily set to 'no' by default. -# -# sanitize-dump-payload no - -# The filename where to dump the DB -dbfilename dump.rdb - -# Remove RDB files used by replication in instances without persistence -# enabled. By default this option is disabled, however there are environments -# where for regulations or other security concerns, RDB files persisted on -# disk by masters in order to feed replicas, or stored on disk by replicas -# in order to load them for the initial synchronization, should be deleted -# ASAP. Note that this option ONLY WORKS in instances that have both AOF -# and RDB persistence disabled, otherwise is completely ignored. -# -# An alternative (and sometimes better) way to obtain the same effect is -# to use diskless replication on both master and replicas instances. However -# in the case of replicas, diskless is not always an option. -rdb-del-sync-files no - -# The working directory. -# -# The DB will be written inside this directory, with the filename specified -# above using the 'dbfilename' configuration directive. -# -# The Append Only File will also be created inside this directory. -# -# Note that you must specify a directory here, not a file name. -dir ./ - -################################# REPLICATION ################################# - -# Master-Replica replication. Use replicaof to make a Redis instance a copy of -# another Redis server. A few things to understand ASAP about Redis replication. -# -# +------------------+ +---------------+ -# | Master | ---> | Replica | -# | (receive writes) | | (exact copy) | -# +------------------+ +---------------+ -# -# 1) Redis replication is asynchronous, but you can configure a master to -# stop accepting writes if it appears to be not connected with at least -# a given number of replicas. -# 2) Redis replicas are able to perform a partial resynchronization with the -# master if the replication link is lost for a relatively small amount of -# time. You may want to configure the replication backlog size (see the next -# sections of this file) with a sensible value depending on your needs. -# 3) Replication is automatic and does not need user intervention. After a -# network partition replicas automatically try to reconnect to masters -# and resynchronize with them. -# -# replicaof - -# If the master is password protected (using the "requirepass" configuration -# directive below) it is possible to tell the replica to authenticate before -# starting the replication synchronization process, otherwise the master will -# refuse the replica request. -# -# masterauth -# -# However this is not enough if you are using Redis ACLs (for Redis version -# 6 or greater), and the default user is not capable of running the PSYNC -# command and/or other commands needed for replication. In this case it's -# better to configure a special user to use with replication, and specify the -# masteruser configuration as such: -# -# masteruser -# -# When masteruser is specified, the replica will authenticate against its -# master using the new AUTH form: AUTH . - -# When a replica loses its connection with the master, or when the replication -# is still in progress, the replica can act in two different ways: -# -# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will -# still reply to client requests, possibly with out of date data, or the -# data set may just be empty if this is the first synchronization. -# -# 2) If replica-serve-stale-data is set to 'no' the replica will reply with error -# "MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'" -# to all data access commands, excluding commands such as: -# INFO, REPLICAOF, AUTH, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE, -# UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST, -# HOST and LATENCY. -# -replica-serve-stale-data yes - -# You can configure a replica instance to accept writes or not. Writing against -# a replica instance may be useful to store some ephemeral data (because data -# written on a replica will be easily deleted after resync with the master) but -# may also cause problems if clients are writing to it because of a -# misconfiguration. -# -# Since Redis 2.6 by default replicas are read-only. -# -# Note: read only replicas are not designed to be exposed to untrusted clients -# on the internet. It's just a protection layer against misuse of the instance. -# Still a read only replica exports by default all the administrative commands -# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve -# security of read only replicas using 'rename-command' to shadow all the -# administrative / dangerous commands. -replica-read-only yes - -# Replication SYNC strategy: disk or socket. -# -# New replicas and reconnecting replicas that are not able to continue the -# replication process just receiving differences, need to do what is called a -# "full synchronization". An RDB file is transmitted from the master to the -# replicas. -# -# The transmission can happen in two different ways: -# -# 1) Disk-backed: The Redis master creates a new process that writes the RDB -# file on disk. Later the file is transferred by the parent -# process to the replicas incrementally. -# 2) Diskless: The Redis master creates a new process that directly writes the -# RDB file to replica sockets, without touching the disk at all. -# -# With disk-backed replication, while the RDB file is generated, more replicas -# can be queued and served with the RDB file as soon as the current child -# producing the RDB file finishes its work. With diskless replication instead -# once the transfer starts, new replicas arriving will be queued and a new -# transfer will start when the current one terminates. -# -# When diskless replication is used, the master waits a configurable amount of -# time (in seconds) before starting the transfer in the hope that multiple -# replicas will arrive and the transfer can be parallelized. -# -# With slow disks and fast (large bandwidth) networks, diskless replication -# works better. -repl-diskless-sync yes - -# When diskless replication is enabled, it is possible to configure the delay -# the server waits in order to spawn the child that transfers the RDB via socket -# to the replicas. -# -# This is important since once the transfer starts, it is not possible to serve -# new replicas arriving, that will be queued for the next RDB transfer, so the -# server waits a delay in order to let more replicas arrive. -# -# The delay is specified in seconds, and by default is 5 seconds. To disable -# it entirely just set it to 0 seconds and the transfer will start ASAP. -repl-diskless-sync-delay 5 - -# When diskless replication is enabled with a delay, it is possible to let -# the replication start before the maximum delay is reached if the maximum -# number of replicas expected have connected. Default of 0 means that the -# maximum is not defined and Redis will wait the full delay. -repl-diskless-sync-max-replicas 0 - -# ----------------------------------------------------------------------------- -# WARNING: RDB diskless load is experimental. Since in this setup the replica -# does not immediately store an RDB on disk, it may cause data loss during -# failovers. RDB diskless load + Redis modules not handling I/O reads may also -# cause Redis to abort in case of I/O errors during the initial synchronization -# stage with the master. Use only if you know what you are doing. -# ----------------------------------------------------------------------------- -# -# Replica can load the RDB it reads from the replication link directly from the -# socket, or store the RDB to a file and read that file after it was completely -# received from the master. -# -# In many cases the disk is slower than the network, and storing and loading -# the RDB file may increase replication time (and even increase the master's -# Copy on Write memory and replica buffers). -# However, parsing the RDB file directly from the socket may mean that we have -# to flush the contents of the current database before the full rdb was -# received. For this reason we have the following options: -# -# "disabled" - Don't use diskless load (store the rdb file to the disk first) -# "on-empty-db" - Use diskless load only when it is completely safe. -# "swapdb" - Keep current db contents in RAM while parsing the data directly -# from the socket. Replicas in this mode can keep serving current -# data set while replication is in progress, except for cases where -# they can't recognize master as having a data set from same -# replication history. -# Note that this requires sufficient memory, if you don't have it, -# you risk an OOM kill. -repl-diskless-load disabled - -# Master send PINGs to its replicas in a predefined interval. It's possible to -# change this interval with the repl_ping_replica_period option. The default -# value is 10 seconds. -# -# repl-ping-replica-period 10 - -# The following option sets the replication timeout for: -# -# 1) Bulk transfer I/O during SYNC, from the point of view of replica. -# 2) Master timeout from the point of view of replicas (data, pings). -# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings). -# -# It is important to make sure that this value is greater than the value -# specified for repl-ping-replica-period otherwise a timeout will be detected -# every time there is low traffic between the master and the replica. The default -# value is 60 seconds. -# -# repl-timeout 60 - -# Disable TCP_NODELAY on the replica socket after SYNC? -# -# If you select "yes" Redis will use a smaller number of TCP packets and -# less bandwidth to send data to replicas. But this can add a delay for -# the data to appear on the replica side, up to 40 milliseconds with -# Linux kernels using a default configuration. -# -# If you select "no" the delay for data to appear on the replica side will -# be reduced but more bandwidth will be used for replication. -# -# By default we optimize for low latency, but in very high traffic conditions -# or when the master and replicas are many hops away, turning this to "yes" may -# be a good idea. -repl-disable-tcp-nodelay no - -# Set the replication backlog size. The backlog is a buffer that accumulates -# replica data when replicas are disconnected for some time, so that when a -# replica wants to reconnect again, often a full resync is not needed, but a -# partial resync is enough, just passing the portion of data the replica -# missed while disconnected. -# -# The bigger the replication backlog, the longer the replica can endure the -# disconnect and later be able to perform a partial resynchronization. -# -# The backlog is only allocated if there is at least one replica connected. -# -# repl-backlog-size 1mb - -# After a master has no connected replicas for some time, the backlog will be -# freed. The following option configures the amount of seconds that need to -# elapse, starting from the time the last replica disconnected, for the backlog -# buffer to be freed. -# -# Note that replicas never free the backlog for timeout, since they may be -# promoted to masters later, and should be able to correctly "partially -# resynchronize" with other replicas: hence they should always accumulate backlog. -# -# A value of 0 means to never release the backlog. -# -# repl-backlog-ttl 3600 - -# The replica priority is an integer number published by Redis in the INFO -# output. It is used by Redis Sentinel in order to select a replica to promote -# into a master if the master is no longer working correctly. -# -# A replica with a low priority number is considered better for promotion, so -# for instance if there are three replicas with priority 10, 100, 25 Sentinel -# will pick the one with priority 10, that is the lowest. -# -# However a special priority of 0 marks the replica as not able to perform the -# role of master, so a replica with priority of 0 will never be selected by -# Redis Sentinel for promotion. -# -# By default the priority is 100. -replica-priority 100 - -# The propagation error behavior controls how Redis will behave when it is -# unable to handle a command being processed in the replication stream from a master -# or processed while reading from an AOF file. Errors that occur during propagation -# are unexpected, and can cause data inconsistency. However, there are edge cases -# in earlier versions of Redis where it was possible for the server to replicate or persist -# commands that would fail on future versions. For this reason the default behavior -# is to ignore such errors and continue processing commands. -# -# If an application wants to ensure there is no data divergence, this configuration -# should be set to 'panic' instead. The value can also be set to 'panic-on-replicas' -# to only panic when a replica encounters an error on the replication stream. One of -# these two panic values will become the default value in the future once there are -# sufficient safety mechanisms in place to prevent false positive crashes. -# -# propagation-error-behavior ignore - -# Replica ignore disk write errors controls the behavior of a replica when it is -# unable to persist a write command received from its master to disk. By default, -# this configuration is set to 'no' and will crash the replica in this condition. -# It is not recommended to change this default, however in order to be compatible -# with older versions of Redis this config can be toggled to 'yes' which will just -# log a warning and execute the write command it got from the master. -# -# replica-ignore-disk-write-errors no - -# ----------------------------------------------------------------------------- -# By default, Redis Sentinel includes all replicas in its reports. A replica -# can be excluded from Redis Sentinel's announcements. An unannounced replica -# will be ignored by the 'sentinel replicas ' command and won't be -# exposed to Redis Sentinel's clients. -# -# This option does not change the behavior of replica-priority. Even with -# replica-announced set to 'no', the replica can be promoted to master. To -# prevent this behavior, set replica-priority to 0. -# -# replica-announced yes - -# It is possible for a master to stop accepting writes if there are less than -# N replicas connected, having a lag less or equal than M seconds. -# -# The N replicas need to be in "online" state. -# -# The lag in seconds, that must be <= the specified value, is calculated from -# the last ping received from the replica, that is usually sent every second. -# -# This option does not GUARANTEE that N replicas will accept the write, but -# will limit the window of exposure for lost writes in case not enough replicas -# are available, to the specified number of seconds. -# -# For example to require at least 3 replicas with a lag <= 10 seconds use: -# -# min-replicas-to-write 3 -# min-replicas-max-lag 10 -# -# Setting one or the other to 0 disables the feature. -# -# By default min-replicas-to-write is set to 0 (feature disabled) and -# min-replicas-max-lag is set to 10. - -# A Redis master is able to list the address and port of the attached -# replicas in different ways. For example the "INFO replication" section -# offers this information, which is used, among other tools, by -# Redis Sentinel in order to discover replica instances. -# Another place where this info is available is in the output of the -# "ROLE" command of a master. -# -# The listed IP address and port normally reported by a replica is -# obtained in the following way: -# -# IP: The address is auto detected by checking the peer address -# of the socket used by the replica to connect with the master. -# -# Port: The port is communicated by the replica during the replication -# handshake, and is normally the port that the replica is using to -# listen for connections. -# -# However when port forwarding or Network Address Translation (NAT) is -# used, the replica may actually be reachable via different IP and port -# pairs. The following two options can be used by a replica in order to -# report to its master a specific set of IP and port, so that both INFO -# and ROLE will report those values. -# -# There is no need to use both the options if you need to override just -# the port or the IP address. -# -# replica-announce-ip 5.5.5.5 -# replica-announce-port 1234 - -############################### KEYS TRACKING ################################# - -# Redis implements server assisted support for client side caching of values. -# This is implemented using an invalidation table that remembers, using -# a radix key indexed by key name, what clients have which keys. In turn -# this is used in order to send invalidation messages to clients. Please -# check this page to understand more about the feature: -# -# https://redis.io/topics/client-side-caching -# -# When tracking is enabled for a client, all the read only queries are assumed -# to be cached: this will force Redis to store information in the invalidation -# table. When keys are modified, such information is flushed away, and -# invalidation messages are sent to the clients. However if the workload is -# heavily dominated by reads, Redis could use more and more memory in order -# to track the keys fetched by many clients. -# -# For this reason it is possible to configure a maximum fill value for the -# invalidation table. By default it is set to 1M of keys, and once this limit -# is reached, Redis will start to evict keys in the invalidation table -# even if they were not modified, just to reclaim memory: this will in turn -# force the clients to invalidate the cached values. Basically the table -# maximum size is a trade off between the memory you want to spend server -# side to track information about who cached what, and the ability of clients -# to retain cached objects in memory. -# -# If you set the value to 0, it means there are no limits, and Redis will -# retain as many keys as needed in the invalidation table. -# In the "stats" INFO section, you can find information about the number of -# keys in the invalidation table at every given moment. -# -# Note: when key tracking is used in broadcasting mode, no memory is used -# in the server side so this setting is useless. -# -# tracking-table-max-keys 1000000 - -################################## SECURITY ################################### - -# Warning: since Redis is pretty fast, an outside user can try up to -# 1 million passwords per second against a modern box. This means that you -# should use very strong passwords, otherwise they will be very easy to break. -# Note that because the password is really a shared secret between the client -# and the server, and should not be memorized by any human, the password -# can be easily a long string from /dev/urandom or whatever, so by using a -# long and unguessable password no brute force attack will be possible. - -# Redis ACL users are defined in the following format: -# -# user ... acl rules ... -# -# For example: -# -# user worker +@list +@connection ~jobs:* on >ffa9203c493aa99 -# -# The special username "default" is used for new connections. If this user -# has the "nopass" rule, then new connections will be immediately authenticated -# as the "default" user without the need of any password provided via the -# AUTH command. Otherwise if the "default" user is not flagged with "nopass" -# the connections will start in not authenticated state, and will require -# AUTH (or the HELLO command AUTH option) in order to be authenticated and -# start to work. -# -# The ACL rules that describe what a user can do are the following: -# -# on Enable the user: it is possible to authenticate as this user. -# off Disable the user: it's no longer possible to authenticate -# with this user, however the already authenticated connections -# will still work. -# skip-sanitize-payload RESTORE dump-payload sanitization is skipped. -# sanitize-payload RESTORE dump-payload is sanitized (default). -# + Allow the execution of that command. -# May be used with `|` for allowing subcommands (e.g "+config|get") -# - Disallow the execution of that command. -# May be used with `|` for blocking subcommands (e.g "-config|set") -# +@ Allow the execution of all the commands in such category -# with valid categories are like @admin, @set, @sortedset, ... -# and so forth, see the full list in the server.c file where -# the Redis command table is described and defined. -# The special category @all means all the commands, but currently -# present in the server, and that will be loaded in the future -# via modules. -# +|first-arg Allow a specific first argument of an otherwise -# disabled command. It is only supported on commands with -# no sub-commands, and is not allowed as negative form -# like -SELECT|1, only additive starting with "+". This -# feature is deprecated and may be removed in the future. -# allcommands Alias for +@all. Note that it implies the ability to execute -# all the future commands loaded via the modules system. -# nocommands Alias for -@all. -# ~ Add a pattern of keys that can be mentioned as part of -# commands. For instance ~* allows all the keys. The pattern -# is a glob-style pattern like the one of KEYS. -# It is possible to specify multiple patterns. -# %R~ Add key read pattern that specifies which keys can be read -# from. -# %W~ Add key write pattern that specifies which keys can be -# written to. -# allkeys Alias for ~* -# resetkeys Flush the list of allowed keys patterns. -# & Add a glob-style pattern of Pub/Sub channels that can be -# accessed by the user. It is possible to specify multiple channel -# patterns. -# allchannels Alias for &* -# resetchannels Flush the list of allowed channel patterns. -# > Add this password to the list of valid password for the user. -# For example >mypass will add "mypass" to the list. -# This directive clears the "nopass" flag (see later). -# < Remove this password from the list of valid passwords. -# nopass All the set passwords of the user are removed, and the user -# is flagged as requiring no password: it means that every -# password will work against this user. If this directive is -# used for the default user, every new connection will be -# immediately authenticated with the default user without -# any explicit AUTH command required. Note that the "resetpass" -# directive will clear this condition. -# resetpass Flush the list of allowed passwords. Moreover removes the -# "nopass" status. After "resetpass" the user has no associated -# passwords and there is no way to authenticate without adding -# some password (or setting it as "nopass" later). -# reset Performs the following actions: resetpass, resetkeys, off, -# -@all. The user returns to the same state it has immediately -# after its creation. -# () Create a new selector with the options specified within the -# parentheses and attach it to the user. Each option should be -# space separated. The first character must be ( and the last -# character must be ). -# clearselectors Remove all of the currently attached selectors. -# Note this does not change the "root" user permissions, -# which are the permissions directly applied onto the -# user (outside the parentheses). -# -# ACL rules can be specified in any order: for instance you can start with -# passwords, then flags, or key patterns. However note that the additive -# and subtractive rules will CHANGE MEANING depending on the ordering. -# For instance see the following example: -# -# user alice on +@all -DEBUG ~* >somepassword -# -# This will allow "alice" to use all the commands with the exception of the -# DEBUG command, since +@all added all the commands to the set of the commands -# alice can use, and later DEBUG was removed. However if we invert the order -# of two ACL rules the result will be different: -# -# user alice on -DEBUG +@all ~* >somepassword -# -# Now DEBUG was removed when alice had yet no commands in the set of allowed -# commands, later all the commands are added, so the user will be able to -# execute everything. -# -# Basically ACL rules are processed left-to-right. -# -# The following is a list of command categories and their meanings: -# * keyspace - Writing or reading from keys, databases, or their metadata -# in a type agnostic way. Includes DEL, RESTORE, DUMP, RENAME, EXISTS, DBSIZE, -# KEYS, EXPIRE, TTL, FLUSHALL, etc. Commands that may modify the keyspace, -# key or metadata will also have `write` category. Commands that only read -# the keyspace, key or metadata will have the `read` category. -# * read - Reading from keys (values or metadata). Note that commands that don't -# interact with keys, will not have either `read` or `write`. -# * write - Writing to keys (values or metadata) -# * admin - Administrative commands. Normal applications will never need to use -# these. Includes REPLICAOF, CONFIG, DEBUG, SAVE, MONITOR, ACL, SHUTDOWN, etc. -# * dangerous - Potentially dangerous (each should be considered with care for -# various reasons). This includes FLUSHALL, MIGRATE, RESTORE, SORT, KEYS, -# CLIENT, DEBUG, INFO, CONFIG, SAVE, REPLICAOF, etc. -# * connection - Commands affecting the connection or other connections. -# This includes AUTH, SELECT, COMMAND, CLIENT, ECHO, PING, etc. -# * blocking - Potentially blocking the connection until released by another -# command. -# * fast - Fast O(1) commands. May loop on the number of arguments, but not the -# number of elements in the key. -# * slow - All commands that are not Fast. -# * pubsub - PUBLISH / SUBSCRIBE related -# * transaction - WATCH / MULTI / EXEC related commands. -# * scripting - Scripting related. -# * set - Data type: sets related. -# * sortedset - Data type: zsets related. -# * list - Data type: lists related. -# * hash - Data type: hashes related. -# * string - Data type: strings related. -# * bitmap - Data type: bitmaps related. -# * hyperloglog - Data type: hyperloglog related. -# * geo - Data type: geo related. -# * stream - Data type: streams related. -# -# For more information about ACL configuration please refer to -# the Redis web site at https://redis.io/topics/acl - -# ACL LOG -# -# The ACL Log tracks failed commands and authentication events associated -# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked -# by ACLs. The ACL Log is stored in memory. You can reclaim memory with -# ACL LOG RESET. Define the maximum entry length of the ACL Log below. -acllog-max-len 128 - -# Using an external ACL file -# -# Instead of configuring users here in this file, it is possible to use -# a stand-alone file just listing users. The two methods cannot be mixed: -# if you configure users here and at the same time you activate the external -# ACL file, the server will refuse to start. -# -# The format of the external ACL user file is exactly the same as the -# format that is used inside redis.conf to describe users. -# -# aclfile /etc/redis/users.acl - -# IMPORTANT NOTE: starting with Redis 6 "requirepass" is just a compatibility -# layer on top of the new ACL system. The option effect will be just setting -# the password for the default user. Clients will still authenticate using -# AUTH as usually, or more explicitly with AUTH default -# if they follow the new protocol: both will work. -# -# The requirepass is not compatible with aclfile option and the ACL LOAD -# command, these will cause requirepass to be ignored. -#redis密码 -requirepass FDXLK3LdGYYk9mut - -# New users are initialized with restrictive permissions by default, via the -# equivalent of this ACL rule 'off resetkeys -@all'. Starting with Redis 6.2, it -# is possible to manage access to Pub/Sub channels with ACL rules as well. The -# default Pub/Sub channels permission if new users is controlled by the -# acl-pubsub-default configuration directive, which accepts one of these values: -# -# allchannels: grants access to all Pub/Sub channels -# resetchannels: revokes access to all Pub/Sub channels -# -# From Redis 7.0, acl-pubsub-default defaults to 'resetchannels' permission. -# -# acl-pubsub-default resetchannels - -# Command renaming (DEPRECATED). -# -# ------------------------------------------------------------------------ -# WARNING: avoid using this option if possible. Instead use ACLs to remove -# commands from the default user, and put them only in some admin user you -# create for administrative purposes. -# ------------------------------------------------------------------------ -# -# It is possible to change the name of dangerous commands in a shared -# environment. For instance the CONFIG command may be renamed into something -# hard to guess so that it will still be available for internal-use tools -# but not available for general clients. -# -# Example: -# -# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 -# -# It is also possible to completely kill a command by renaming it into -# an empty string: -# -# rename-command CONFIG "" -# -# Please note that changing the name of commands that are logged into the -# AOF file or transmitted to replicas may cause problems. - -################################### CLIENTS #################################### - -# Set the max number of connected clients at the same time. By default -# this limit is set to 10000 clients, however if the Redis server is not -# able to configure the process file limit to allow for the specified limit -# the max number of allowed clients is set to the current file limit -# minus 32 (as Redis reserves a few file descriptors for internal uses). -# -# Once the limit is reached Redis will close all the new connections sending -# an error 'max number of clients reached'. -# -# IMPORTANT: When Redis Cluster is used, the max number of connections is also -# shared with the cluster bus: every node in the cluster will use two -# connections, one incoming and another outgoing. It is important to size the -# limit accordingly in case of very large clusters. -# -# maxclients 10000 - -############################## MEMORY MANAGEMENT ################################ - -# Set a memory usage limit to the specified amount of bytes. -# When the memory limit is reached Redis will try to remove keys -# according to the eviction policy selected (see maxmemory-policy). -# -# If Redis can't remove keys according to the policy, or if the policy is -# set to 'noeviction', Redis will start to reply with errors to commands -# that would use more memory, like SET, LPUSH, and so on, and will continue -# to reply to read-only commands like GET. -# -# This option is usually useful when using Redis as an LRU or LFU cache, or to -# set a hard memory limit for an instance (using the 'noeviction' policy). -# -# WARNING: If you have replicas attached to an instance with maxmemory on, -# the size of the output buffers needed to feed the replicas are subtracted -# from the used memory count, so that network problems / resyncs will -# not trigger a loop where keys are evicted, and in turn the output -# buffer of replicas is full with DELs of keys evicted triggering the deletion -# of more keys, and so forth until the database is completely emptied. -# -# In short... if you have replicas attached it is suggested that you set a lower -# limit for maxmemory so that there is some free RAM on the system for replica -# output buffers (but this is not needed if the policy is 'noeviction'). -# -# maxmemory - -# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory -# is reached. You can select one from the following behaviors: -# -# volatile-lru -> Evict using approximated LRU, only keys with an expire set. -# allkeys-lru -> Evict any key using approximated LRU. -# volatile-lfu -> Evict using approximated LFU, only keys with an expire set. -# allkeys-lfu -> Evict any key using approximated LFU. -# volatile-random -> Remove a random key having an expire set. -# allkeys-random -> Remove a random key, any key. -# volatile-ttl -> Remove the key with the nearest expire time (minor TTL) -# noeviction -> Don't evict anything, just return an error on write operations. -# -# LRU means Least Recently Used -# LFU means Least Frequently Used -# -# Both LRU, LFU and volatile-ttl are implemented using approximated -# randomized algorithms. -# -# Note: with any of the above policies, when there are no suitable keys for -# eviction, Redis will return an error on write operations that require -# more memory. These are usually commands that create new keys, add data or -# modify existing keys. A few examples are: SET, INCR, HSET, LPUSH, SUNIONSTORE, -# SORT (due to the STORE argument), and EXEC (if the transaction includes any -# command that requires memory). -# -# The default is: -# -# maxmemory-policy noeviction - -# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated -# algorithms (in order to save memory), so you can tune it for speed or -# accuracy. By default Redis will check five keys and pick the one that was -# used least recently, you can change the sample size using the following -# configuration directive. -# -# The default of 5 produces good enough results. 10 Approximates very closely -# true LRU but costs more CPU. 3 is faster but not very accurate. -# -# maxmemory-samples 5 - -# Eviction processing is designed to function well with the default setting. -# If there is an unusually large amount of write traffic, this value may need to -# be increased. Decreasing this value may reduce latency at the risk of -# eviction processing effectiveness -# 0 = minimum latency, 10 = default, 100 = process without regard to latency -# -# maxmemory-eviction-tenacity 10 - -# Starting from Redis 5, by default a replica will ignore its maxmemory setting -# (unless it is promoted to master after a failover or manually). It means -# that the eviction of keys will be just handled by the master, sending the -# DEL commands to the replica as keys evict in the master side. -# -# This behavior ensures that masters and replicas stay consistent, and is usually -# what you want, however if your replica is writable, or you want the replica -# to have a different memory setting, and you are sure all the writes performed -# to the replica are idempotent, then you may change this default (but be sure -# to understand what you are doing). -# -# Note that since the replica by default does not evict, it may end using more -# memory than the one set via maxmemory (there are certain buffers that may -# be larger on the replica, or data structures may sometimes take more memory -# and so forth). So make sure you monitor your replicas and make sure they -# have enough memory to never hit a real out-of-memory condition before the -# master hits the configured maxmemory setting. -# -# replica-ignore-maxmemory yes - -# Redis reclaims expired keys in two ways: upon access when those keys are -# found to be expired, and also in background, in what is called the -# "active expire key". The key space is slowly and interactively scanned -# looking for expired keys to reclaim, so that it is possible to free memory -# of keys that are expired and will never be accessed again in a short time. -# -# The default effort of the expire cycle will try to avoid having more than -# ten percent of expired keys still in memory, and will try to avoid consuming -# more than 25% of total memory and to add latency to the system. However -# it is possible to increase the expire "effort" that is normally set to -# "1", to a greater value, up to the value "10". At its maximum value the -# system will use more CPU, longer cycles (and technically may introduce -# more latency), and will tolerate less already expired keys still present -# in the system. It's a tradeoff between memory, CPU and latency. -# -# active-expire-effort 1 - -############################# LAZY FREEING #################################### - -# Redis has two primitives to delete keys. One is called DEL and is a blocking -# deletion of the object. It means that the server stops processing new commands -# in order to reclaim all the memory associated with an object in a synchronous -# way. If the key deleted is associated with a small object, the time needed -# in order to execute the DEL command is very small and comparable to most other -# O(1) or O(log_N) commands in Redis. However if the key is associated with an -# aggregated value containing millions of elements, the server can block for -# a long time (even seconds) in order to complete the operation. -# -# For the above reasons Redis also offers non blocking deletion primitives -# such as UNLINK (non blocking DEL) and the ASYNC option of FLUSHALL and -# FLUSHDB commands, in order to reclaim memory in background. Those commands -# are executed in constant time. Another thread will incrementally free the -# object in the background as fast as possible. -# -# DEL, UNLINK and ASYNC option of FLUSHALL and FLUSHDB are user-controlled. -# It's up to the design of the application to understand when it is a good -# idea to use one or the other. However the Redis server sometimes has to -# delete keys or flush the whole database as a side effect of other operations. -# Specifically Redis deletes objects independently of a user call in the -# following scenarios: -# -# 1) On eviction, because of the maxmemory and maxmemory policy configurations, -# in order to make room for new data, without going over the specified -# memory limit. -# 2) Because of expire: when a key with an associated time to live (see the -# EXPIRE command) must be deleted from memory. -# 3) Because of a side effect of a command that stores data on a key that may -# already exist. For example the RENAME command may delete the old key -# content when it is replaced with another one. Similarly SUNIONSTORE -# or SORT with STORE option may delete existing keys. The SET command -# itself removes any old content of the specified key in order to replace -# it with the specified string. -# 4) During replication, when a replica performs a full resynchronization with -# its master, the content of the whole database is removed in order to -# load the RDB file just transferred. -# -# In all the above cases the default is to delete objects in a blocking way, -# like if DEL was called. However you can configure each case specifically -# in order to instead release memory in a non-blocking way like if UNLINK -# was called, using the following configuration directives. - -lazyfree-lazy-eviction no -lazyfree-lazy-expire no -lazyfree-lazy-server-del no -replica-lazy-flush no - -# It is also possible, for the case when to replace the user code DEL calls -# with UNLINK calls is not easy, to modify the default behavior of the DEL -# command to act exactly like UNLINK, using the following configuration -# directive: - -lazyfree-lazy-user-del no - -# FLUSHDB, FLUSHALL, SCRIPT FLUSH and FUNCTION FLUSH support both asynchronous and synchronous -# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the -# commands. When neither flag is passed, this directive will be used to determine -# if the data should be deleted asynchronously. - -lazyfree-lazy-user-flush no - -################################ THREADED I/O ################################# - -# Redis is mostly single threaded, however there are certain threaded -# operations such as UNLINK, slow I/O accesses and other things that are -# performed on side threads. -# -# Now it is also possible to handle Redis clients socket reads and writes -# in different I/O threads. Since especially writing is so slow, normally -# Redis users use pipelining in order to speed up the Redis performances per -# core, and spawn multiple instances in order to scale more. Using I/O -# threads it is possible to easily speedup two times Redis without resorting -# to pipelining nor sharding of the instance. -# -# By default threading is disabled, we suggest enabling it only in machines -# that have at least 4 or more cores, leaving at least one spare core. -# Using more than 8 threads is unlikely to help much. We also recommend using -# threaded I/O only if you actually have performance problems, with Redis -# instances being able to use a quite big percentage of CPU time, otherwise -# there is no point in using this feature. -# -# So for instance if you have a four cores boxes, try to use 2 or 3 I/O -# threads, if you have a 8 cores, try to use 6 threads. In order to -# enable I/O threads use the following configuration directive: -# -# io-threads 4 -# -# Setting io-threads to 1 will just use the main thread as usual. -# When I/O threads are enabled, we only use threads for writes, that is -# to thread the write(2) syscall and transfer the client buffers to the -# socket. However it is also possible to enable threading of reads and -# protocol parsing using the following configuration directive, by setting -# it to yes: -# -# io-threads-do-reads no -# -# Usually threading reads doesn't help much. -# -# NOTE 1: This configuration directive cannot be changed at runtime via -# CONFIG SET. Also, this feature currently does not work when SSL is -# enabled. -# -# NOTE 2: If you want to test the Redis speedup using redis-benchmark, make -# sure you also run the benchmark itself in threaded mode, using the -# --threads option to match the number of Redis threads, otherwise you'll not -# be able to notice the improvements. - -############################ KERNEL OOM CONTROL ############################## - -# On Linux, it is possible to hint the kernel OOM killer on what processes -# should be killed first when out of memory. -# -# Enabling this feature makes Redis actively control the oom_score_adj value -# for all its processes, depending on their role. The default scores will -# attempt to have background child processes killed before all others, and -# replicas killed before masters. -# -# Redis supports these options: -# -# no: Don't make changes to oom-score-adj (default). -# yes: Alias to "relative" see below. -# absolute: Values in oom-score-adj-values are written as is to the kernel. -# relative: Values are used relative to the initial value of oom_score_adj when -# the server starts and are then clamped to a range of -1000 to 1000. -# Because typically the initial value is 0, they will often match the -# absolute values. -oom-score-adj no - -# When oom-score-adj is used, this directive controls the specific values used -# for master, replica and background child processes. Values range -2000 to -# 2000 (higher means more likely to be killed). -# -# Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities) -# can freely increase their value, but not decrease it below its initial -# settings. This means that setting oom-score-adj to "relative" and setting the -# oom-score-adj-values to positive values will always succeed. -oom-score-adj-values 0 200 800 - - -#################### KERNEL transparent hugepage CONTROL ###################### - -# Usually the kernel Transparent Huge Pages control is set to "madvise" or -# or "never" by default (/sys/kernel/mm/transparent_hugepage/enabled), in which -# case this config has no effect. On systems in which it is set to "always", -# redis will attempt to disable it specifically for the redis process in order -# to avoid latency problems specifically with fork(2) and CoW. -# If for some reason you prefer to keep it enabled, you can set this config to -# "no" and the kernel global to "always". - -disable-thp yes - -############################## APPEND ONLY MODE ############################### - -# By default Redis asynchronously dumps the dataset on disk. This mode is -# good enough in many applications, but an issue with the Redis process or -# a power outage may result into a few minutes of writes lost (depending on -# the configured save points). -# -# The Append Only File is an alternative persistence mode that provides -# much better durability. For instance using the default data fsync policy -# (see later in the config file) Redis can lose just one second of writes in a -# dramatic event like a server power outage, or a single write if something -# wrong with the Redis process itself happens, but the operating system is -# still running correctly. -# -# AOF and RDB persistence can be enabled at the same time without problems. -# If the AOF is enabled on startup Redis will load the AOF, that is the file -# with the better durability guarantees. -# -# Please check https://redis.io/topics/persistence for more information. -# 开启redis持久化 -appendonly yes - -# The base name of the append only file. -# -# Redis 7 and newer use a set of append-only files to persist the dataset -# and changes applied to it. There are two basic types of files in use: -# -# - Base files, which are a snapshot representing the complete state of the -# dataset at the time the file was created. Base files can be either in -# the form of RDB (binary serialized) or AOF (textual commands). -# - Incremental files, which contain additional commands that were applied -# to the dataset following the previous file. -# -# In addition, manifest files are used to track the files and the order in -# which they were created and should be applied. -# -# Append-only file names are created by Redis following a specific pattern. -# The file name's prefix is based on the 'appendfilename' configuration -# parameter, followed by additional information about the sequence and type. -# -# For example, if appendfilename is set to appendonly.aof, the following file -# names could be derived: -# -# - appendonly.aof.1.base.rdb as a base file. -# - appendonly.aof.1.incr.aof, appendonly.aof.2.incr.aof as incremental files. -# - appendonly.aof.manifest as a manifest file. - -appendfilename "appendonly.aof" - -# For convenience, Redis stores all persistent append-only files in a dedicated -# directory. The name of the directory is determined by the appenddirname -# configuration parameter. - -appenddirname "appendonlydir" - -# The fsync() call tells the Operating System to actually write data on disk -# instead of waiting for more data in the output buffer. Some OS will really flush -# data on disk, some other OS will just try to do it ASAP. -# -# Redis supports three different modes: -# -# no: don't fsync, just let the OS flush the data when it wants. Faster. -# always: fsync after every write to the append only log. Slow, Safest. -# everysec: fsync only one time every second. Compromise. -# -# The default is "everysec", as that's usually the right compromise between -# speed and data safety. It's up to you to understand if you can relax this to -# "no" that will let the operating system flush the output buffer when -# it wants, for better performances (but if you can live with the idea of -# some data loss consider the default persistence mode that's snapshotting), -# or on the contrary, use "always" that's very slow but a bit safer than -# everysec. -# -# More details please check the following article: -# http://antirez.com/post/redis-persistence-demystified.html -# -# If unsure, use "everysec". - -# appendfsync always -appendfsync everysec -# appendfsync no - -# When the AOF fsync policy is set to always or everysec, and a background -# saving process (a background save or AOF log background rewriting) is -# performing a lot of I/O against the disk, in some Linux configurations -# Redis may block too long on the fsync() call. Note that there is no fix for -# this currently, as even performing fsync in a different thread will block -# our synchronous write(2) call. -# -# In order to mitigate this problem it's possible to use the following option -# that will prevent fsync() from being called in the main process while a -# BGSAVE or BGREWRITEAOF is in progress. -# -# This means that while another child is saving, the durability of Redis is -# the same as "appendfsync no". In practical terms, this means that it is -# possible to lose up to 30 seconds of log in the worst scenario (with the -# default Linux settings). -# -# If you have latency problems turn this to "yes". Otherwise leave it as -# "no" that is the safest pick from the point of view of durability. - -no-appendfsync-on-rewrite no - -# Automatic rewrite of the append only file. -# Redis is able to automatically rewrite the log file implicitly calling -# BGREWRITEAOF when the AOF log size grows by the specified percentage. -# -# This is how it works: Redis remembers the size of the AOF file after the -# latest rewrite (if no rewrite has happened since the restart, the size of -# the AOF at startup is used). -# -# This base size is compared to the current size. If the current size is -# bigger than the specified percentage, the rewrite is triggered. Also -# you need to specify a minimal size for the AOF file to be rewritten, this -# is useful to avoid rewriting the AOF file even if the percentage increase -# is reached but it is still pretty small. -# -# Specify a percentage of zero in order to disable the automatic AOF -# rewrite feature. - -auto-aof-rewrite-percentage 100 -auto-aof-rewrite-min-size 64mb - -# An AOF file may be found to be truncated at the end during the Redis -# startup process, when the AOF data gets loaded back into memory. -# This may happen when the system where Redis is running -# crashes, especially when an ext4 filesystem is mounted without the -# data=ordered option (however this can't happen when Redis itself -# crashes or aborts but the operating system still works correctly). -# -# Redis can either exit with an error when this happens, or load as much -# data as possible (the default now) and start if the AOF file is found -# to be truncated at the end. The following option controls this behavior. -# -# If aof-load-truncated is set to yes, a truncated AOF file is loaded and -# the Redis server starts emitting a log to inform the user of the event. -# Otherwise if the option is set to no, the server aborts with an error -# and refuses to start. When the option is set to no, the user requires -# to fix the AOF file using the "redis-check-aof" utility before to restart -# the server. -# -# Note that if the AOF file will be found to be corrupted in the middle -# the server will still exit with an error. This option only applies when -# Redis will try to read more data from the AOF file but not enough bytes -# will be found. -aof-load-truncated yes - -# Redis can create append-only base files in either RDB or AOF formats. Using -# the RDB format is always faster and more efficient, and disabling it is only -# supported for backward compatibility purposes. -aof-use-rdb-preamble yes - -# Redis supports recording timestamp annotations in the AOF to support restoring -# the data from a specific point-in-time. However, using this capability changes -# the AOF format in a way that may not be compatible with existing AOF parsers. -aof-timestamp-enabled no - -################################ SHUTDOWN ##################################### - -# Maximum time to wait for replicas when shutting down, in seconds. -# -# During shut down, a grace period allows any lagging replicas to catch up with -# the latest replication offset before the master exists. This period can -# prevent data loss, especially for deployments without configured disk backups. -# -# The 'shutdown-timeout' value is the grace period's duration in seconds. It is -# only applicable when the instance has replicas. To disable the feature, set -# the value to 0. -# -# shutdown-timeout 10 - -# When Redis receives a SIGINT or SIGTERM, shutdown is initiated and by default -# an RDB snapshot is written to disk in a blocking operation if save points are configured. -# The options used on signaled shutdown can include the following values: -# default: Saves RDB snapshot only if save points are configured. -# Waits for lagging replicas to catch up. -# save: Forces a DB saving operation even if no save points are configured. -# nosave: Prevents DB saving operation even if one or more save points are configured. -# now: Skips waiting for lagging replicas. -# force: Ignores any errors that would normally prevent the server from exiting. -# -# Any combination of values is allowed as long as "save" and "nosave" are not set simultaneously. -# Example: "nosave force now" -# -# shutdown-on-sigint default -# shutdown-on-sigterm default - -################ NON-DETERMINISTIC LONG BLOCKING COMMANDS ##################### - -# Maximum time in milliseconds for EVAL scripts, functions and in some cases -# modules' commands before Redis can start processing or rejecting other clients. -# -# If the maximum execution time is reached Redis will start to reply to most -# commands with a BUSY error. -# -# In this state Redis will only allow a handful of commands to be executed. -# For instance, SCRIPT KILL, FUNCTION KILL, SHUTDOWN NOSAVE and possibly some -# module specific 'allow-busy' commands. -# -# SCRIPT KILL and FUNCTION KILL will only be able to stop a script that did not -# yet call any write commands, so SHUTDOWN NOSAVE may be the only way to stop -# the server in the case a write command was already issued by the script when -# the user doesn't want to wait for the natural termination of the script. -# -# The default is 5 seconds. It is possible to set it to 0 or a negative value -# to disable this mechanism (uninterrupted execution). Note that in the past -# this config had a different name, which is now an alias, so both of these do -# the same: -# lua-time-limit 5000 -# busy-reply-threshold 5000 - -################################ REDIS CLUSTER ############################### - -# Normal Redis instances can't be part of a Redis Cluster; only nodes that are -# started as cluster nodes can. In order to start a Redis instance as a -# cluster node enable the cluster support uncommenting the following: -# -# cluster-enabled yes - -# Every cluster node has a cluster configuration file. This file is not -# intended to be edited by hand. It is created and updated by Redis nodes. -# Every Redis Cluster node requires a different cluster configuration file. -# Make sure that instances running in the same system do not have -# overlapping cluster configuration file names. -# -# cluster-config-file nodes-6379.conf - -# Cluster node timeout is the amount of milliseconds a node must be unreachable -# for it to be considered in failure state. -# Most other internal time limits are a multiple of the node timeout. -# -# cluster-node-timeout 15000 - -# The cluster port is the port that the cluster bus will listen for inbound connections on. When set -# to the default value, 0, it will be bound to the command port + 10000. Setting this value requires -# you to specify the cluster bus port when executing cluster meet. -# cluster-port 0 - -# A replica of a failing master will avoid to start a failover if its data -# looks too old. -# -# There is no simple way for a replica to actually have an exact measure of -# its "data age", so the following two checks are performed: -# -# 1) If there are multiple replicas able to failover, they exchange messages -# in order to try to give an advantage to the replica with the best -# replication offset (more data from the master processed). -# Replicas will try to get their rank by offset, and apply to the start -# of the failover a delay proportional to their rank. -# -# 2) Every single replica computes the time of the last interaction with -# its master. This can be the last ping or command received (if the master -# is still in the "connected" state), or the time that elapsed since the -# disconnection with the master (if the replication link is currently down). -# If the last interaction is too old, the replica will not try to failover -# at all. -# -# The point "2" can be tuned by user. Specifically a replica will not perform -# the failover if, since the last interaction with the master, the time -# elapsed is greater than: -# -# (node-timeout * cluster-replica-validity-factor) + repl-ping-replica-period -# -# So for example if node-timeout is 30 seconds, and the cluster-replica-validity-factor -# is 10, and assuming a default repl-ping-replica-period of 10 seconds, the -# replica will not try to failover if it was not able to talk with the master -# for longer than 310 seconds. -# -# A large cluster-replica-validity-factor may allow replicas with too old data to failover -# a master, while a too small value may prevent the cluster from being able to -# elect a replica at all. -# -# For maximum availability, it is possible to set the cluster-replica-validity-factor -# to a value of 0, which means, that replicas will always try to failover the -# master regardless of the last time they interacted with the master. -# (However they'll always try to apply a delay proportional to their -# offset rank). -# -# Zero is the only value able to guarantee that when all the partitions heal -# the cluster will always be able to continue. -# -# cluster-replica-validity-factor 10 - -# Cluster replicas are able to migrate to orphaned masters, that are masters -# that are left without working replicas. This improves the cluster ability -# to resist to failures as otherwise an orphaned master can't be failed over -# in case of failure if it has no working replicas. -# -# Replicas migrate to orphaned masters only if there are still at least a -# given number of other working replicas for their old master. This number -# is the "migration barrier". A migration barrier of 1 means that a replica -# will migrate only if there is at least 1 other working replica for its master -# and so forth. It usually reflects the number of replicas you want for every -# master in your cluster. -# -# Default is 1 (replicas migrate only if their masters remain with at least -# one replica). To disable migration just set it to a very large value or -# set cluster-allow-replica-migration to 'no'. -# A value of 0 can be set but is useful only for debugging and dangerous -# in production. -# -# cluster-migration-barrier 1 - -# Turning off this option allows to use less automatic cluster configuration. -# It both disables migration to orphaned masters and migration from masters -# that became empty. -# -# Default is 'yes' (allow automatic migrations). -# -# cluster-allow-replica-migration yes - -# By default Redis Cluster nodes stop accepting queries if they detect there -# is at least a hash slot uncovered (no available node is serving it). -# This way if the cluster is partially down (for example a range of hash slots -# are no longer covered) all the cluster becomes, eventually, unavailable. -# It automatically returns available as soon as all the slots are covered again. -# -# However sometimes you want the subset of the cluster which is working, -# to continue to accept queries for the part of the key space that is still -# covered. In order to do so, just set the cluster-require-full-coverage -# option to no. -# -# cluster-require-full-coverage yes - -# This option, when set to yes, prevents replicas from trying to failover its -# master during master failures. However the replica can still perform a -# manual failover, if forced to do so. -# -# This is useful in different scenarios, especially in the case of multiple -# data center operations, where we want one side to never be promoted if not -# in the case of a total DC failure. -# -# cluster-replica-no-failover no - -# This option, when set to yes, allows nodes to serve read traffic while the -# cluster is in a down state, as long as it believes it owns the slots. -# -# This is useful for two cases. The first case is for when an application -# doesn't require consistency of data during node failures or network partitions. -# One example of this is a cache, where as long as the node has the data it -# should be able to serve it. -# -# The second use case is for configurations that don't meet the recommended -# three shards but want to enable cluster mode and scale later. A -# master outage in a 1 or 2 shard configuration causes a read/write outage to the -# entire cluster without this option set, with it set there is only a write outage. -# Without a quorum of masters, slot ownership will not change automatically. -# -# cluster-allow-reads-when-down no - -# This option, when set to yes, allows nodes to serve pubsub shard traffic while -# the cluster is in a down state, as long as it believes it owns the slots. -# -# This is useful if the application would like to use the pubsub feature even when -# the cluster global stable state is not OK. If the application wants to make sure only -# one shard is serving a given channel, this feature should be kept as yes. -# -# cluster-allow-pubsubshard-when-down yes - -# Cluster link send buffer limit is the limit on the memory usage of an individual -# cluster bus link's send buffer in bytes. Cluster links would be freed if they exceed -# this limit. This is to primarily prevent send buffers from growing unbounded on links -# toward slow peers (E.g. PubSub messages being piled up). -# This limit is disabled by default. Enable this limit when 'mem_cluster_links' INFO field -# and/or 'send-buffer-allocated' entries in the 'CLUSTER LINKS` command output continuously increase. -# Minimum limit of 1gb is recommended so that cluster link buffer can fit in at least a single -# PubSub message by default. (client-query-buffer-limit default value is 1gb) -# -# cluster-link-sendbuf-limit 0 - -# Clusters can configure their announced hostname using this config. This is a common use case for -# applications that need to use TLS Server Name Indication (SNI) or dealing with DNS based -# routing. By default this value is only shown as additional metadata in the CLUSTER SLOTS -# command, but can be changed using 'cluster-preferred-endpoint-type' config. This value is -# communicated along the clusterbus to all nodes, setting it to an empty string will remove -# the hostname and also propagate the removal. -# -# cluster-announce-hostname "" - -# Clusters can advertise how clients should connect to them using either their IP address, -# a user defined hostname, or by declaring they have no endpoint. Which endpoint is -# shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type -# config with values 'ip', 'hostname', or 'unknown-endpoint'. This value controls how -# the endpoint returned for MOVED/ASKING requests as well as the first field of CLUSTER SLOTS. -# If the preferred endpoint type is set to hostname, but no announced hostname is set, a '?' -# will be returned instead. -# -# When a cluster advertises itself as having an unknown endpoint, it's indicating that -# the server doesn't know how clients can reach the cluster. This can happen in certain -# networking situations where there are multiple possible routes to the node, and the -# server doesn't know which one the client took. In this case, the server is expecting -# the client to reach out on the same endpoint it used for making the last request, but use -# the port provided in the response. -# -# cluster-preferred-endpoint-type ip - -# In order to setup your cluster make sure to read the documentation -# available at https://redis.io web site. - -########################## CLUSTER DOCKER/NAT support ######################## - -# In certain deployments, Redis Cluster nodes address discovery fails, because -# addresses are NAT-ted or because ports are forwarded (the typical case is -# Docker and other containers). -# -# In order to make Redis Cluster working in such environments, a static -# configuration where each node knows its public address is needed. The -# following four options are used for this scope, and are: -# -# * cluster-announce-ip -# * cluster-announce-port -# * cluster-announce-tls-port -# * cluster-announce-bus-port -# -# Each instructs the node about its address, client ports (for connections -# without and with TLS) and cluster message bus port. The information is then -# published in the header of the bus packets so that other nodes will be able to -# correctly map the address of the node publishing the information. -# -# If cluster-tls is set to yes and cluster-announce-tls-port is omitted or set -# to zero, then cluster-announce-port refers to the TLS port. Note also that -# cluster-announce-tls-port has no effect if cluster-tls is set to no. -# -# If the above options are not used, the normal Redis Cluster auto-detection -# will be used instead. -# -# Note that when remapped, the bus port may not be at the fixed offset of -# clients port + 10000, so you can specify any port and bus-port depending -# on how they get remapped. If the bus-port is not set, a fixed offset of -# 10000 will be used as usual. -# -# Example: -# -# cluster-announce-ip 10.1.1.5 -# cluster-announce-tls-port 6379 -# cluster-announce-port 0 -# cluster-announce-bus-port 6380 - -################################## SLOW LOG ################################### - -# The Redis Slow Log is a system to log queries that exceeded a specified -# execution time. The execution time does not include the I/O operations -# like talking with the client, sending the reply and so forth, -# but just the time needed to actually execute the command (this is the only -# stage of command execution where the thread is blocked and can not serve -# other requests in the meantime). -# -# You can configure the slow log with two parameters: one tells Redis -# what is the execution time, in microseconds, to exceed in order for the -# command to get logged, and the other parameter is the length of the -# slow log. When a new command is logged the oldest one is removed from the -# queue of logged commands. - -# The following time is expressed in microseconds, so 1000000 is equivalent -# to one second. Note that a negative number disables the slow log, while -# a value of zero forces the logging of every command. -slowlog-log-slower-than 10000 - -# There is no limit to this length. Just be aware that it will consume memory. -# You can reclaim memory used by the slow log with SLOWLOG RESET. -slowlog-max-len 128 - -################################ LATENCY MONITOR ############################## - -# The Redis latency monitoring subsystem samples different operations -# at runtime in order to collect data related to possible sources of -# latency of a Redis instance. -# -# Via the LATENCY command this information is available to the user that can -# print graphs and obtain reports. -# -# The system only logs operations that were performed in a time equal or -# greater than the amount of milliseconds specified via the -# latency-monitor-threshold configuration directive. When its value is set -# to zero, the latency monitor is turned off. -# -# By default latency monitoring is disabled since it is mostly not needed -# if you don't have latency issues, and collecting data has a performance -# impact, that while very small, can be measured under big load. Latency -# monitoring can easily be enabled at runtime using the command -# "CONFIG SET latency-monitor-threshold " if needed. -latency-monitor-threshold 0 - -################################ LATENCY TRACKING ############################## - -# The Redis extended latency monitoring tracks the per command latencies and enables -# exporting the percentile distribution via the INFO latencystats command, -# and cumulative latency distributions (histograms) via the LATENCY command. -# -# By default, the extended latency monitoring is enabled since the overhead -# of keeping track of the command latency is very small. -# latency-tracking yes - -# By default the exported latency percentiles via the INFO latencystats command -# are the p50, p99, and p999. -# latency-tracking-info-percentiles 50 99 99.9 - -############################# EVENT NOTIFICATION ############################## - -# Redis can notify Pub/Sub clients about events happening in the key space. -# This feature is documented at https://redis.io/topics/notifications -# -# For instance if keyspace events notification is enabled, and a client -# performs a DEL operation on key "foo" stored in the Database 0, two -# messages will be published via Pub/Sub: -# -# PUBLISH __keyspace@0__:foo del -# PUBLISH __keyevent@0__:del foo -# -# It is possible to select the events that Redis will notify among a set -# of classes. Every class is identified by a single character: -# -# K Keyspace events, published with __keyspace@__ prefix. -# E Keyevent events, published with __keyevent@__ prefix. -# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ... -# $ String commands -# l List commands -# s Set commands -# h Hash commands -# z Sorted set commands -# x Expired events (events generated every time a key expires) -# e Evicted events (events generated when a key is evicted for maxmemory) -# n New key events (Note: not included in the 'A' class) -# t Stream commands -# d Module key type events -# m Key-miss events (Note: It is not included in the 'A' class) -# A Alias for g$lshzxetd, so that the "AKE" string means all the events -# (Except key-miss events which are excluded from 'A' due to their -# unique nature). -# -# The "notify-keyspace-events" takes as argument a string that is composed -# of zero or multiple characters. The empty string means that notifications -# are disabled. -# -# Example: to enable list and generic events, from the point of view of the -# event name, use: -# -# notify-keyspace-events Elg -# -# Example 2: to get the stream of the expired keys subscribing to channel -# name __keyevent@0__:expired use: -# -# notify-keyspace-events Ex -# -# By default all notifications are disabled because most users don't need -# this feature and the feature has some overhead. Note that if you don't -# specify at least one of K or E, no events will be delivered. -notify-keyspace-events "" - -############################### ADVANCED CONFIG ############################### - -# Hashes are encoded using a memory efficient data structure when they have a -# small number of entries, and the biggest entry does not exceed a given -# threshold. These thresholds can be configured using the following directives. -hash-max-listpack-entries 512 -hash-max-listpack-value 64 - -# Lists are also encoded in a special way to save a lot of space. -# The number of entries allowed per internal list node can be specified -# as a fixed maximum size or a maximum number of elements. -# For a fixed maximum size, use -5 through -1, meaning: -# -5: max size: 64 Kb <-- not recommended for normal workloads -# -4: max size: 32 Kb <-- not recommended -# -3: max size: 16 Kb <-- probably not recommended -# -2: max size: 8 Kb <-- good -# -1: max size: 4 Kb <-- good -# Positive numbers mean store up to _exactly_ that number of elements -# per list node. -# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size), -# but if your use case is unique, adjust the settings as necessary. -list-max-listpack-size -2 - -# Lists may also be compressed. -# Compress depth is the number of quicklist ziplist nodes from *each* side of -# the list to *exclude* from compression. The head and tail of the list -# are always uncompressed for fast push/pop operations. Settings are: -# 0: disable all list compression -# 1: depth 1 means "don't start compressing until after 1 node into the list, -# going from either the head or tail" -# So: [head]->node->node->...->node->[tail] -# [head], [tail] will always be uncompressed; inner nodes will compress. -# 2: [head]->[next]->node->node->...->node->[prev]->[tail] -# 2 here means: don't compress head or head->next or tail->prev or tail, -# but compress all nodes between them. -# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail] -# etc. -list-compress-depth 0 - -# Sets have a special encoding in just one case: when a set is composed -# of just strings that happen to be integers in radix 10 in the range -# of 64 bit signed integers. -# The following configuration setting sets the limit in the size of the -# set in order to use this special memory saving encoding. -set-max-intset-entries 512 - -# Similarly to hashes and lists, sorted sets are also specially encoded in -# order to save a lot of space. This encoding is only used when the length and -# elements of a sorted set are below the following limits: -zset-max-listpack-entries 128 -zset-max-listpack-value 64 - -# HyperLogLog sparse representation bytes limit. The limit includes the -# 16 bytes header. When an HyperLogLog using the sparse representation crosses -# this limit, it is converted into the dense representation. -# -# A value greater than 16000 is totally useless, since at that point the -# dense representation is more memory efficient. -# -# The suggested value is ~ 3000 in order to have the benefits of -# the space efficient encoding without slowing down too much PFADD, -# which is O(N) with the sparse encoding. The value can be raised to -# ~ 10000 when CPU is not a concern, but space is, and the data set is -# composed of many HyperLogLogs with cardinality in the 0 - 15000 range. -hll-sparse-max-bytes 3000 - -# Streams macro node max size / items. The stream data structure is a radix -# tree of big nodes that encode multiple items inside. Using this configuration -# it is possible to configure how big a single node can be in bytes, and the -# maximum number of items it may contain before switching to a new node when -# appending new stream entries. If any of the following settings are set to -# zero, the limit is ignored, so for instance it is possible to set just a -# max entries limit by setting max-bytes to 0 and max-entries to the desired -# value. -stream-node-max-bytes 4096 -stream-node-max-entries 100 - -# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in -# order to help rehashing the main Redis hash table (the one mapping top-level -# keys to values). The hash table implementation Redis uses (see dict.c) -# performs a lazy rehashing: the more operation you run into a hash table -# that is rehashing, the more rehashing "steps" are performed, so if the -# server is idle the rehashing is never complete and some more memory is used -# by the hash table. -# -# The default is to use this millisecond 10 times every second in order to -# actively rehash the main dictionaries, freeing memory when possible. -# -# If unsure: -# use "activerehashing no" if you have hard latency requirements and it is -# not a good thing in your environment that Redis can reply from time to time -# to queries with 2 milliseconds delay. -# -# use "activerehashing yes" if you don't have such hard requirements but -# want to free memory asap when possible. -activerehashing yes - -# The client output buffer limits can be used to force disconnection of clients -# that are not reading data from the server fast enough for some reason (a -# common reason is that a Pub/Sub client can't consume messages as fast as the -# publisher can produce them). -# -# The limit can be set differently for the three different classes of clients: -# -# normal -> normal clients including MONITOR clients -# replica -> replica clients -# pubsub -> clients subscribed to at least one pubsub channel or pattern -# -# The syntax of every client-output-buffer-limit directive is the following: -# -# client-output-buffer-limit -# -# A client is immediately disconnected once the hard limit is reached, or if -# the soft limit is reached and remains reached for the specified number of -# seconds (continuously). -# So for instance if the hard limit is 32 megabytes and the soft limit is -# 16 megabytes / 10 seconds, the client will get disconnected immediately -# if the size of the output buffers reach 32 megabytes, but will also get -# disconnected if the client reaches 16 megabytes and continuously overcomes -# the limit for 10 seconds. -# -# By default normal clients are not limited because they don't receive data -# without asking (in a push way), but just after a request, so only -# asynchronous clients may create a scenario where data is requested faster -# than it can read. -# -# Instead there is a default limit for pubsub and replica clients, since -# subscribers and replicas receive data in a push fashion. -# -# Note that it doesn't make sense to set the replica clients output buffer -# limit lower than the repl-backlog-size config (partial sync will succeed -# and then replica will get disconnected). -# Such a configuration is ignored (the size of repl-backlog-size will be used). -# This doesn't have memory consumption implications since the replica client -# will share the backlog buffers memory. -# -# Both the hard or the soft limit can be disabled by setting them to zero. -client-output-buffer-limit normal 0 0 0 -client-output-buffer-limit replica 256mb 64mb 60 -client-output-buffer-limit pubsub 32mb 8mb 60 - -# Client query buffers accumulate new commands. They are limited to a fixed -# amount by default in order to avoid that a protocol desynchronization (for -# instance due to a bug in the client) will lead to unbound memory usage in -# the query buffer. However you can configure it here if you have very special -# needs, such us huge multi/exec requests or alike. -# -# client-query-buffer-limit 1gb - -# In some scenarios client connections can hog up memory leading to OOM -# errors or data eviction. To avoid this we can cap the accumulated memory -# used by all client connections (all pubsub and normal clients). Once we -# reach that limit connections will be dropped by the server freeing up -# memory. The server will attempt to drop the connections using the most -# memory first. We call this mechanism "client eviction". -# -# Client eviction is configured using the maxmemory-clients setting as follows: -# 0 - client eviction is disabled (default) -# -# A memory value can be used for the client eviction threshold, -# for example: -# maxmemory-clients 1g -# -# A percentage value (between 1% and 100%) means the client eviction threshold -# is based on a percentage of the maxmemory setting. For example to set client -# eviction at 5% of maxmemory: -# maxmemory-clients 5% - -# In the Redis protocol, bulk requests, that are, elements representing single -# strings, are normally limited to 512 mb. However you can change this limit -# here, but must be 1mb or greater -# -# proto-max-bulk-len 512mb - -# Redis calls an internal function to perform many background tasks, like -# closing connections of clients in timeout, purging expired keys that are -# never requested, and so forth. -# -# Not all tasks are performed with the same frequency, but Redis checks for -# tasks to perform according to the specified "hz" value. -# -# By default "hz" is set to 10. Raising the value will use more CPU when -# Redis is idle, but at the same time will make Redis more responsive when -# there are many keys expiring at the same time, and timeouts may be -# handled with more precision. -# -# The range is between 1 and 500, however a value over 100 is usually not -# a good idea. Most users should use the default of 10 and raise this up to -# 100 only in environments where very low latency is required. -hz 10 - -# Normally it is useful to have an HZ value which is proportional to the -# number of clients connected. This is useful in order, for instance, to -# avoid too many clients are processed for each background task invocation -# in order to avoid latency spikes. -# -# Since the default HZ value by default is conservatively set to 10, Redis -# offers, and enables by default, the ability to use an adaptive HZ value -# which will temporarily raise when there are many connected clients. -# -# When dynamic HZ is enabled, the actual configured HZ will be used -# as a baseline, but multiples of the configured HZ value will be actually -# used as needed once more clients are connected. In this way an idle -# instance will use very little CPU time while a busy instance will be -# more responsive. -dynamic-hz yes - -# When a child rewrites the AOF file, if the following option is enabled -# the file will be fsync-ed every 4 MB of data generated. This is useful -# in order to commit the file to the disk more incrementally and avoid -# big latency spikes. -aof-rewrite-incremental-fsync yes - -# When redis saves RDB file, if the following option is enabled -# the file will be fsync-ed every 4 MB of data generated. This is useful -# in order to commit the file to the disk more incrementally and avoid -# big latency spikes. -rdb-save-incremental-fsync yes - -# Redis LFU eviction (see maxmemory setting) can be tuned. However it is a good -# idea to start with the default settings and only change them after investigating -# how to improve the performances and how the keys LFU change over time, which -# is possible to inspect via the OBJECT FREQ command. -# -# There are two tunable parameters in the Redis LFU implementation: the -# counter logarithm factor and the counter decay time. It is important to -# understand what the two parameters mean before changing them. -# -# The LFU counter is just 8 bits per key, it's maximum value is 255, so Redis -# uses a probabilistic increment with logarithmic behavior. Given the value -# of the old counter, when a key is accessed, the counter is incremented in -# this way: -# -# 1. A random number R between 0 and 1 is extracted. -# 2. A probability P is calculated as 1/(old_value*lfu_log_factor+1). -# 3. The counter is incremented only if R < P. -# -# The default lfu-log-factor is 10. This is a table of how the frequency -# counter changes with a different number of accesses with different -# logarithmic factors: -# -# +--------+------------+------------+------------+------------+------------+ -# | factor | 100 hits | 1000 hits | 100K hits | 1M hits | 10M hits | -# +--------+------------+------------+------------+------------+------------+ -# | 0 | 104 | 255 | 255 | 255 | 255 | -# +--------+------------+------------+------------+------------+------------+ -# | 1 | 18 | 49 | 255 | 255 | 255 | -# +--------+------------+------------+------------+------------+------------+ -# | 10 | 10 | 18 | 142 | 255 | 255 | -# +--------+------------+------------+------------+------------+------------+ -# | 100 | 8 | 11 | 49 | 143 | 255 | -# +--------+------------+------------+------------+------------+------------+ -# -# NOTE: The above table was obtained by running the following commands: -# -# redis-benchmark -n 1000000 incr foo -# redis-cli object freq foo -# -# NOTE 2: The counter initial value is 5 in order to give new objects a chance -# to accumulate hits. -# -# The counter decay time is the time, in minutes, that must elapse in order -# for the key counter to be divided by two (or decremented if it has a value -# less <= 10). -# -# The default value for the lfu-decay-time is 1. A special value of 0 means to -# decay the counter every time it happens to be scanned. -# -# lfu-log-factor 10 -# lfu-decay-time 1 - -########################### ACTIVE DEFRAGMENTATION ####################### -# -# What is active defragmentation? -# ------------------------------- -# -# Active (online) defragmentation allows a Redis server to compact the -# spaces left between small allocations and deallocations of data in memory, -# thus allowing to reclaim back memory. -# -# Fragmentation is a natural process that happens with every allocator (but -# less so with Jemalloc, fortunately) and certain workloads. Normally a server -# restart is needed in order to lower the fragmentation, or at least to flush -# away all the data and create it again. However thanks to this feature -# implemented by Oran Agra for Redis 4.0 this process can happen at runtime -# in a "hot" way, while the server is running. -# -# Basically when the fragmentation is over a certain level (see the -# configuration options below) Redis will start to create new copies of the -# values in contiguous memory regions by exploiting certain specific Jemalloc -# features (in order to understand if an allocation is causing fragmentation -# and to allocate it in a better place), and at the same time, will release the -# old copies of the data. This process, repeated incrementally for all the keys -# will cause the fragmentation to drop back to normal values. -# -# Important things to understand: -# -# 1. This feature is disabled by default, and only works if you compiled Redis -# to use the copy of Jemalloc we ship with the source code of Redis. -# This is the default with Linux builds. -# -# 2. You never need to enable this feature if you don't have fragmentation -# issues. -# -# 3. Once you experience fragmentation, you can enable this feature when -# needed with the command "CONFIG SET activedefrag yes". -# -# The configuration parameters are able to fine tune the behavior of the -# defragmentation process. If you are not sure about what they mean it is -# a good idea to leave the defaults untouched. - -# Active defragmentation is disabled by default -# activedefrag no - -# Minimum amount of fragmentation waste to start active defrag -# active-defrag-ignore-bytes 100mb - -# Minimum percentage of fragmentation to start active defrag -# active-defrag-threshold-lower 10 - -# Maximum percentage of fragmentation at which we use maximum effort -# active-defrag-threshold-upper 100 - -# Minimal effort for defrag in CPU percentage, to be used when the lower -# threshold is reached -# active-defrag-cycle-min 1 - -# Maximal effort for defrag in CPU percentage, to be used when the upper -# threshold is reached -# active-defrag-cycle-max 25 - -# Maximum number of set/hash/zset/list fields that will be processed from -# the main dictionary scan -# active-defrag-max-scan-fields 1000 - -# Jemalloc background thread for purging will be enabled by default -jemalloc-bg-thread yes - -# It is possible to pin different threads and processes of Redis to specific -# CPUs in your system, in order to maximize the performances of the server. -# This is useful both in order to pin different Redis threads in different -# CPUs, but also in order to make sure that multiple Redis instances running -# in the same host will be pinned to different CPUs. -# -# Normally you can do this using the "taskset" command, however it is also -# possible to this via Redis configuration directly, both in Linux and FreeBSD. -# -# You can pin the server/IO threads, bio threads, aof rewrite child process, and -# the bgsave child process. The syntax to specify the cpu list is the same as -# the taskset command: -# -# Set redis server/io threads to cpu affinity 0,2,4,6: -# server_cpulist 0-7:2 -# -# Set bio threads to cpu affinity 1,3: -# bio_cpulist 1,3 -# -# Set aof rewrite child process to cpu affinity 8,9,10,11: -# aof_rewrite_cpulist 8-11 -# -# Set bgsave child process to cpu affinity 1,10,11 -# bgsave_cpulist 1,10-11 - -# In some cases redis will emit warnings and even refuse to start if it detects -# that the system is in bad state, it is possible to suppress these warnings -# by setting the following config which takes a space delimited list of warnings -# to suppress -# -# ignore-warnings ARM64-COW-BUG diff --git a/manifest/docker/Dockerfile b/manifest/docker/Dockerfile new file mode 100644 index 0000000..d3abe8f --- /dev/null +++ b/manifest/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM loads/alpine:3.8 + +############################################################################### +# INSTALLATION +############################################################################### + +ENV WORKDIR /app +ADD resource $WORKDIR/ +ADD ./temp/linux_amd64/main $WORKDIR/main +RUN chmod +x $WORKDIR/main + +############################################################################### +# START +############################################################################### +WORKDIR $WORKDIR +CMD ./main diff --git a/manifest/docker/docker-compose.yml b/manifest/docker/docker-compose.yml new file mode 100644 index 0000000..c6da693 --- /dev/null +++ b/manifest/docker/docker-compose.yml @@ -0,0 +1,13 @@ +version: "3" +services: + asynqmon: + image: hibiken/asynqmon:master + ports: + - '3302:8080' + command: + - --redis-url=redis://192.168.5.105:6379/0 + deploy: + resources: + limits: + cpus: '0.50' + memory: '1024M' \ No newline at end of file diff --git a/manifest/docker/docker.sh b/manifest/docker/docker.sh new file mode 100644 index 0000000..ff393f9 --- /dev/null +++ b/manifest/docker/docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This shell is executed before docker build. + + + + + diff --git a/manifest/i18n/.gitkeep b/manifest/i18n/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/manifest/protobuf/.keep-if-necessary b/manifest/protobuf/.keep-if-necessary new file mode 100644 index 0000000..e69de29 diff --git a/module/hello/api/hello/hello.go b/module/hello/api/hello/hello.go new file mode 100644 index 0000000..92596dc --- /dev/null +++ b/module/hello/api/hello/hello.go @@ -0,0 +1,15 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package hello + +import ( + "context" + + "sagooiot/module/hello/api/hello/v1" +) + +type IHelloV1 interface { + Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) +} diff --git a/module/hello/api/hello/v1/hello.go b/module/hello/api/hello/v1/hello.go new file mode 100644 index 0000000..b4dd233 --- /dev/null +++ b/module/hello/api/hello/v1/hello.go @@ -0,0 +1,12 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +type HelloReq struct { + g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"` +} +type HelloRes struct { + g.Meta `mime:"text/html" example:"string"` +} diff --git a/module/hello/controller/hello/hello.go b/module/hello/controller/hello/hello.go new file mode 100644 index 0000000..bd9895e --- /dev/null +++ b/module/hello/controller/hello/hello.go @@ -0,0 +1,15 @@ +// ================================================================================= +// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish. +// ================================================================================= + +package hello + +import ( + "sagooiot/module/hello/api/hello" +) + +type ControllerV1 struct{} + +func NewV1() hello.IHelloV1 { + return &ControllerV1{} +} diff --git a/module/hello/controller/hello/hello_v1_hello.go b/module/hello/controller/hello/hello_v1_hello.go new file mode 100644 index 0000000..e4a3ba4 --- /dev/null +++ b/module/hello/controller/hello/hello_v1_hello.go @@ -0,0 +1,24 @@ +package hello + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/module/hello/service" + + "sagooiot/module/hello/api/hello/v1" +) + +func (c *ControllerV1) Hello(ctx context.Context, req *v1.HelloReq) (res *v1.HelloRes, err error) { + g.RequestFromCtx(ctx).Get("name") + + sysContext := g.RequestFromCtx(ctx) + g.Log().Debug(ctx, sysContext) + + data, err := service.Hello().Speak(ctx, "hello===========") + if err != nil { + g.RequestFromCtx(ctx).Response.Writeln(data) + + } + + return +} diff --git a/module/hello/logic/hello/hello.go b/module/hello/logic/hello/hello.go new file mode 100644 index 0000000..9484573 --- /dev/null +++ b/module/hello/logic/hello/hello.go @@ -0,0 +1,22 @@ +package hello + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/module/hello/service" +) + +type sHello struct{} + +func init() { + service.RegisterHello(helloNew()) +} + +func helloNew() *sHello { + return &sHello{} +} + +func (s *sHello) Speak(ctx context.Context, doc string) (out string, err error) { + g.Log().Debug(ctx, "hello", doc) + return +} diff --git a/module/hello/logic/logic.go b/module/hello/logic/logic.go new file mode 100644 index 0000000..c31b089 --- /dev/null +++ b/module/hello/logic/logic.go @@ -0,0 +1,5 @@ +package logic + +import ( + _ "sagooiot/module/hello/logic/hello" +) diff --git a/module/hello/router.go b/module/hello/router.go new file mode 100644 index 0000000..b372a69 --- /dev/null +++ b/module/hello/router.go @@ -0,0 +1,19 @@ +package hello + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + helloController "sagooiot/module/hello/controller/hello" + _ "sagooiot/module/hello/logic/hello" +) + +// Router 加载模块的路由 +func Router(ctx context.Context, group *ghttp.RouterGroup) { + //访问的地址为:http://127.0.0.1:8199/api/v1/hello + group.Group("/", func(group *ghttp.RouterGroup) { + group.Bind( + helloController.ControllerV1{}, // 获取大屏项目数据 + ) + }) + +} diff --git a/module/hello/service/hello.go b/module/hello/service/hello.go new file mode 100644 index 0000000..d9d0c6e --- /dev/null +++ b/module/hello/service/hello.go @@ -0,0 +1,31 @@ +// ================================================================================ +// Code generated by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" +) + +type ( + IHello interface { + Speak(ctx context.Context, doc string) (out string, err error) + } +) + +var ( + localHello IHello +) + +func Hello() IHello { + if localHello == nil { + panic("implement not found for interface IHello, forgot register?") + } + return localHello +} + +func RegisterHello(i IHello) { + localHello = i +} diff --git a/module/module.go b/module/module.go new file mode 100644 index 0000000..47911a5 --- /dev/null +++ b/module/module.go @@ -0,0 +1,29 @@ +package module + +import ( + "context" + "github.com/gogf/gf/v2/net/ghttp" + "sagooiot/module/hello" +) + +// Router 加载模块的普通路由 +func Router(ctx context.Context, group *ghttp.RouterGroup) { + hello.Router(ctx, group) // 加载hello模块路由 + +} + +// OpenAPIRouter 加载模块的OpenAPI路由 +func OpenAPIRouter(ctx context.Context, group *ghttp.RouterGroup) { + hello.Router(ctx, group) // 加载hello模块路由 + +} + +// NorthRouter 加载模块的北向路由 +func NorthRouter(ctx context.Context, group *ghttp.RouterGroup) { + hello.Router(ctx, group) // 加载hello模块路由 + +} + +func WorkerRun() { + +} diff --git a/module/readme.md b/module/readme.md new file mode 100644 index 0000000..be1f0d5 --- /dev/null +++ b/module/readme.md @@ -0,0 +1,23 @@ +# 模块应用示例 + + +## 工具使用 + +生成Service + +`./gf gen service -s './module/hello/logic' -d './module/hello/service` + + +## 注意事项 + +在写单例测试的时候,要注意引用相关的包,否则会出现错误 + +例如:task_test.go 文件的引用,需要引用如下包 +```go + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + _ "github.com/gogf/gf/contrib/nosql/redis/v2" + _ "sagooiot/module/simcard/logic/sim" + +``` + + diff --git a/network/codebin/bytes.go b/network/codebin/bytes.go index 69f74fb..496d853 100644 --- a/network/codebin/bytes.go +++ b/network/codebin/bytes.go @@ -2,7 +2,7 @@ package codebin import "math" -//ParseUint64 解析 +// ParseUint64 解析 func ParseUint64(b []byte) uint64 { return (uint64(b[0]) << 56) | (uint64(b[1]) << 48) | @@ -14,7 +14,7 @@ func ParseUint64(b []byte) uint64 { uint64(b[7]) } -//ParseUint64LittleEndian 解析 +// ParseUint64LittleEndian 解析 func ParseUint64LittleEndian(b []byte) uint64 { return (uint64(b[7]) << 56) | (uint64(b[6]) << 48) | @@ -26,7 +26,7 @@ func ParseUint64LittleEndian(b []byte) uint64 { uint64(b[0]) } -//ParseUint32 解析 +// ParseUint32 解析 func ParseUint32(buf []byte) uint32 { return uint32(buf[0])<<24 + uint32(buf[1])<<16 + @@ -34,7 +34,7 @@ func ParseUint32(buf []byte) uint32 { uint32(buf[3]) } -//ParseUint32LittleEndian 解析 +// ParseUint32LittleEndian 解析 func ParseUint32LittleEndian(buf []byte) uint32 { return uint32(buf[3])<<24 + uint32(buf[2])<<16 + @@ -42,41 +42,41 @@ func ParseUint32LittleEndian(buf []byte) uint32 { uint32(buf[0]) } -//ParseUint16 解析 +// ParseUint16 解析 func ParseUint16(buf []byte) uint16 { return uint16(buf[0])<<8 + uint16(buf[1]) } -//ParseUint16LittleEndian 解析 +// ParseUint16LittleEndian 解析 func ParseUint16LittleEndian(buf []byte) uint16 { return uint16(buf[1])<<8 + uint16(buf[0]) } -//ParseFloat32 解析 +// ParseFloat32 解析 func ParseFloat32(buf []byte) float32 { val := ParseUint32(buf) return math.Float32frombits(val) } -//ParseFloat32LittleEndian 解析 +// ParseFloat32LittleEndian 解析 func ParseFloat32LittleEndian(buf []byte) float32 { val := ParseUint32LittleEndian(buf) return math.Float32frombits(val) } -//ParseFloat64 解析 +// ParseFloat64 解析 func ParseFloat64(buf []byte) float64 { val := ParseUint64(buf) return math.Float64frombits(val) } -//ParseFloat64LittleEndian 解析 +// ParseFloat64LittleEndian 解析 func ParseFloat64LittleEndian(buf []byte) float64 { val := ParseUint64LittleEndian(buf) return math.Float64frombits(val) } -//Uint32ToBytes 编码 +// Uint32ToBytes 编码 func Uint32ToBytes(value uint32) []byte { buf := make([]byte, 4) buf[0] = byte(value >> 24) @@ -86,7 +86,7 @@ func Uint32ToBytes(value uint32) []byte { return buf } -//Uint32ToBytesLittleEndian 编码 +// Uint32ToBytesLittleEndian 编码 func Uint32ToBytesLittleEndian(value uint32) []byte { buf := make([]byte, 4) buf[3] = byte(value >> 24) @@ -96,7 +96,7 @@ func Uint32ToBytesLittleEndian(value uint32) []byte { return buf } -//Uint16ToBytes 编码 +// Uint16ToBytes 编码 func Uint16ToBytes(value uint16) []byte { buf := make([]byte, 2) buf[0] = byte(value >> 8) @@ -104,7 +104,7 @@ func Uint16ToBytes(value uint16) []byte { return buf } -//Uint16ToBytesLittleEndian 编码 +// Uint16ToBytesLittleEndian 编码 func Uint16ToBytesLittleEndian(value uint16) []byte { buf := make([]byte, 2) buf[1] = byte(value >> 8) @@ -112,7 +112,7 @@ func Uint16ToBytesLittleEndian(value uint16) []byte { return buf } -//WriteUint64 编码 +// WriteUint64 编码 func WriteUint64(buf []byte, value uint64) { buf[0] = byte(value >> 56) buf[1] = byte(value >> 48) @@ -124,7 +124,7 @@ func WriteUint64(buf []byte, value uint64) { buf[7] = byte(value) } -//WriteUint64LittleEndian 编码 +// WriteUint64LittleEndian 编码 func WriteUint64LittleEndian(buf []byte, value uint64) { buf[7] = byte(value >> 56) buf[6] = byte(value >> 48) @@ -136,7 +136,7 @@ func WriteUint64LittleEndian(buf []byte, value uint64) { buf[0] = byte(value) } -//WriteUint32 编码 +// WriteUint32 编码 func WriteUint32(buf []byte, value uint32) { buf[0] = byte(value >> 24) buf[1] = byte(value >> 16) @@ -144,7 +144,7 @@ func WriteUint32(buf []byte, value uint32) { buf[3] = byte(value) } -//WriteUint32LittleEndian 编码 +// WriteUint32LittleEndian 编码 func WriteUint32LittleEndian(buf []byte, value uint32) { buf[3] = byte(value >> 24) buf[2] = byte(value >> 16) @@ -152,57 +152,57 @@ func WriteUint32LittleEndian(buf []byte, value uint32) { buf[0] = byte(value) } -//WriteUint24 编码 +// WriteUint24 编码 func WriteUint24(buf []byte, value uint32) { buf[0] = byte(value >> 16) buf[1] = byte(value >> 8) buf[2] = byte(value) } -//WriteUint24LittleEndian 编码 +// WriteUint24LittleEndian 编码 func WriteUint24LittleEndian(buf []byte, value uint32) { buf[2] = byte(value >> 16) buf[1] = byte(value >> 8) buf[0] = byte(value) } -//WriteUint16 编码 +// WriteUint16 编码 func WriteUint16(buf []byte, value uint16) { buf[0] = byte(value >> 8) buf[1] = byte(value) } -//WriteUint16LittleEndian 编码 +// WriteUint16LittleEndian 编码 func WriteUint16LittleEndian(buf []byte, value uint16) { buf[1] = byte(value >> 8) buf[0] = byte(value) } -//WriteFloat32 编码 +// WriteFloat32 编码 func WriteFloat32(buf []byte, value float32) { val := math.Float32bits(value) WriteUint32(buf, val) } -//WriteFloat32LittleEndian 编码 +// WriteFloat32LittleEndian 编码 func WriteFloat32LittleEndian(buf []byte, value float32) { val := math.Float32bits(value) WriteUint32LittleEndian(buf, val) } -//WriteFloat64 编码 +// WriteFloat64 编码 func WriteFloat64(buf []byte, value float64) { val := math.Float64bits(value) WriteUint64(buf, val) } -//WriteFloat64LittleEndian 编码 +// WriteFloat64LittleEndian 编码 func WriteFloat64LittleEndian(buf []byte, value float64) { val := math.Float64bits(value) WriteUint64LittleEndian(buf, val) } -//BoolToAscii 编码 +// BoolToAscii 编码 func BoolToAscii(buf []byte) []byte { length := len(buf) ret := make([]byte, length) @@ -216,7 +216,7 @@ func BoolToAscii(buf []byte) []byte { return ret } -//AsciiToBool 编码 +// AsciiToBool 编码 func AsciiToBool(buf []byte) []byte { length := len(buf) ret := make([]byte, length) @@ -230,14 +230,14 @@ func AsciiToBool(buf []byte) []byte { return ret } -//Dup 复制 +// Dup 复制 func Dup(buf []byte) []byte { b := make([]byte, len(buf)) copy(b, buf) return b } -//BoolToByte 编码 +// BoolToByte 编码 func BoolToByte(buf []bool) []byte { r := make([]byte, len(buf)) for i, v := range buf { @@ -248,7 +248,7 @@ func BoolToByte(buf []bool) []byte { return r } -//ByteToBool 编码 +// ByteToBool 编码 func ByteToBool(buf []byte) []bool { r := make([]bool, len(buf)) for i, v := range buf { @@ -259,7 +259,7 @@ func ByteToBool(buf []byte) []bool { return r } -//ShrinkBool 压缩布尔类型 +// ShrinkBool 压缩布尔类型 func ShrinkBool(buf []byte) []byte { length := len(buf) //length = length % 8 == 0 ? length / 8 : length / 8 + 1; @@ -280,7 +280,7 @@ func ShrinkBool(buf []byte) []byte { return b } -//ExpandBool 展开布尔类型 +// ExpandBool 展开布尔类型 func ExpandBool(buf []byte, count int) []byte { length := len(buf) ln := length << 3 // length * 8 diff --git a/network/codebin/check.go b/network/codebin/check.go index 038d7e6..e20ab79 100644 --- a/network/codebin/check.go +++ b/network/codebin/check.go @@ -1,6 +1,6 @@ package codebin -//Sum 和 +// Sum 和 func Sum(buf []byte) byte { var sum byte = 0 l := len(buf) @@ -10,7 +10,7 @@ func Sum(buf []byte) byte { return sum } -//Xor 异或 +// Xor 异或 func Xor(buf []byte) byte { var xor = buf[0] l := len(buf) diff --git a/network/codebin/hex.go b/network/codebin/hex.go index d0e8b2b..f4ec86b 100644 --- a/network/codebin/hex.go +++ b/network/codebin/hex.go @@ -4,7 +4,7 @@ import "encoding/hex" var hexNumbers = []byte("0123456789ABCDEF") -//ByteToHex 编码 +// ByteToHex 编码 func ByteToHex(value byte) []byte { buf := make([]byte, 2) buf[0] = hexNumbers[value>>4] @@ -12,19 +12,19 @@ func ByteToHex(value byte) []byte { return buf } -//WriteByteHex 编码 +// WriteByteHex 编码 func WriteByteHex(buf []byte, value uint8) { buf[0] = hexNumbers[value>>4] buf[1] = hexNumbers[value&0x0F] } -//WriteUint8Hex 编码 +// WriteUint8Hex 编码 func WriteUint8Hex(buf []byte, value uint8) { buf[0] = hexNumbers[value>>4] buf[1] = hexNumbers[value&0x0F] } -//WriteUint16Hex 编码 +// WriteUint16Hex 编码 func WriteUint16Hex(buf []byte, value uint16) { h, l := value>>8, value&0xF buf[0] = hexNumbers[h>>4] @@ -34,7 +34,7 @@ func WriteUint16Hex(buf []byte, value uint16) { } -//ToHex 编码 +// ToHex 编码 func ToHex(values []byte) []byte { length := len(values) buf := make([]byte, length<<1) //length * 2 @@ -47,7 +47,7 @@ func ToHex(values []byte) []byte { return buf } -//FromHex 解码 +// FromHex 解码 func FromHex(values []byte) []byte { buf := make([]byte, len(values)>>1) //length / 2 _, _ = hex.Decode(buf, values) diff --git a/network/core/connect.go b/network/core/connect.go deleted file mode 100644 index 9a8bc95..0000000 --- a/network/core/connect.go +++ /dev/null @@ -1 +0,0 @@ -package core diff --git a/network/core/device/device-action.go b/network/core/device/device-action.go new file mode 100644 index 0000000..fd64c2b --- /dev/null +++ b/network/core/device/device-action.go @@ -0,0 +1,67 @@ +package device + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/service" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/network/model" + "time" +) + +func StartAction(ctx context.Context, deviceKey string) { + deviceRes, err := service.DevDevice().Get(ctx, deviceKey) + if err != nil { + g.Log().Error(ctx, err) + return + } + deviceDetail := GetDevice(uint64(deviceRes.Id)) + if deviceDetail != nil { + err = deviceDetail.Start(ctx) + if err != nil { + g.Log().Error(ctx, err) + return + } else { + if deviceRes != nil && deviceRes.Status != consts.DeviceStatueOnline { + if deviceOnlineErr := baseLogic.Online(ctx, model.DeviceOnlineMessage{ + DeviceKey: deviceRes.DevDevice.Key, + ProductKey: deviceRes.Product.Key, + Timestamp: time.Now().Unix(), + }); deviceOnlineErr != nil { + g.Log().Errorf(ctx, "device online error:%v", deviceOnlineErr) + } + return + } + } + } +} + +func OffAction(ctx context.Context, tunnelId int) { + tunnelInfo, err := service.NetworkTunnel().GetTunnelById(ctx, tunnelId) + + //TODO log error + if err == nil && tunnelInfo != nil { + deviceDetail, _ := service.DevDevice().Get(ctx, tunnelInfo.DeviceKey) + if deviceDetail == nil { + g.Log().Errorf(ctx, "deviceKey:%s not found,ignore", tunnelInfo.DeviceKey) + return + } + if deviceStopError := GetDevice(uint64(deviceDetail.Id)).Stop(); deviceStopError != nil { + g.Log().Errorf(ctx, "Stop device error:%v ,ignore", deviceStopError) + } + if deviceDetail != nil && deviceDetail.Status == consts.DeviceStatueOnline { + if deviceOnlineErr := baseLogic.Offline(ctx, model.DeviceOfflineMessage{ + DeviceKey: tunnelInfo.DeviceKey, + ProductKey: deviceDetail.Product.Key, + Timestamp: time.Now().Unix(), + }); deviceOnlineErr != nil { + g.Log().Errorf(ctx, "device online error:%v", deviceOnlineErr) + } + } + + } else if err != nil { + g.Log().Errorf(ctx, "getTunnel ifno error:%v ,ignore", err) + } + +} diff --git a/network/core/device-manager.go b/network/core/device/device-manager.go similarity index 64% rename from network/core/device-manager.go rename to network/core/device/device-manager.go index af1c0f5..fc444cc 100644 --- a/network/core/device-manager.go +++ b/network/core/device/device-manager.go @@ -1,13 +1,12 @@ -package core +package device import ( "context" - logicModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" - "sync" - "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/service" + "sagooiot/network/core/mapper" + "sagooiot/network/model" + "sync" ) var allDevices sync.Map @@ -29,8 +28,12 @@ func RemoveDevice(id uint64) error { return nil //error } +func AddDevice(deviceKey string, device *Device) { + allDevices.Store(deviceKey, device) +} + func LoadDevices(ctx context.Context) error { - listDevice, err := service.DevDevice().List(ctx, &logicModel.ListDeviceInput{}) + listDevice, err := service.DevDevice().List(ctx, "", "") if err != nil { return err } @@ -39,7 +42,7 @@ func LoadDevices(ctx context.Context) error { } devices := make([]*model.Device, len(listDevice)) for index, node := range listDevice { - d := MapperDevice(*node) + d := mapper.Device(*node) devices[index] = &d } for index := range devices { @@ -51,22 +54,22 @@ func LoadDevices(ctx context.Context) error { g.Log().Error(ctx, err) return nil } - allDevices.Store(devices[index].Id, dev) + AddDevice(devices[index].ProductKey, dev) } return nil } -func LoadDevice(ctx context.Context, id uint) (*Device, error) { - deviceInfo, err := service.DevDevice().Detail(ctx, id) +func LoadDevice(ctx context.Context, key string) (*Device, error) { + deviceInfo, err := service.DevDevice().Detail(ctx, key) if err != nil { return nil, err } - mDeviceInfo := MapperDevice(*deviceInfo) + mDeviceInfo := mapper.Device(*deviceInfo) dev, err := NewDevice(ctx, &mDeviceInfo) if err != nil { return dev, err } - allDevices.Store(id, dev) + AddDevice(key, dev) err = dev.Start(ctx) return dev, nil } diff --git a/network/core/device.go b/network/core/device/device.go similarity index 73% rename from network/core/device.go rename to network/core/device/device.go index b48ca3b..d7b4087 100644 --- a/network/core/device.go +++ b/network/core/device/device.go @@ -1,10 +1,11 @@ -package core +package device import ( "context" "errors" - "github.com/sagoo-cloud/sagooiot/network/events" - "github.com/sagoo-cloud/sagooiot/network/model" + "sagooiot/network/core" + "sagooiot/network/events" + "sagooiot/network/model" ) // Device 设备 @@ -22,20 +23,19 @@ type Device struct { commandIndex map[string]*model.Command running bool - tunnel *Tunnel } func NewDevice(ctx context.Context, m *model.Device) (*Device, error) { dev := &Device{ Device: *m, Context: make(map[string]interface{}), - commandIndex: make(map[string]*model.Command, 0), + commandIndex: make(map[string]*model.Command), pollers: make([]*Poller, 0), } //加载产品 var err error - dev.product, err = LoadProduct(ctx, dev.ProductId) + dev.product, err = core.LoadProduct(ctx, dev.ProductKey) if err != nil { return nil, err } @@ -53,14 +53,6 @@ func NewDevice(ctx context.Context, m *model.Device) (*Device, error) { return dev, nil } -func (dev *Device) BindTunnel(tunnel *Tunnel) error { - if tunnel == nil { - return errors.New("通道未加载") - } - dev.tunnel = tunnel - return nil -} - func (dev *Device) onData(data map[string]interface{}) { //向上广播 @@ -68,14 +60,6 @@ func (dev *Device) onData(data map[string]interface{}) { } func (dev *Device) Start(ctx context.Context) error { - tunnel := GetTunnel(int(dev.TunnelId)) - if tunnel == nil { - return errors.New("找不到链接") - } - err := dev.BindTunnel(tunnel) - if err != nil { - return err - } for _, poller := range dev.pollers { err := poller.Start(ctx) if err != nil { diff --git a/network/core/device/point.go b/network/core/device/point.go new file mode 100644 index 0000000..9586835 --- /dev/null +++ b/network/core/device/point.go @@ -0,0 +1,9 @@ +package device + +import ( + "sagooiot/network/model" +) + +type Point struct { + model.Point +} diff --git a/network/core/poller.go b/network/core/device/poller.go similarity index 58% rename from network/core/poller.go rename to network/core/device/poller.go index b5e8e26..8cac36e 100644 --- a/network/core/poller.go +++ b/network/core/device/poller.go @@ -1,30 +1,39 @@ -package core +package device import ( "context" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/network/model" - "github.com/sagoo-cloud/sagooiot/network/pkg/cron" + "github.com/gogf/gf/v2/os/gcron" + "sagooiot/network/model" + //"sagooiot/network/pkg/cron" ) +//TODO 采集器的定时任务需要与系统的定时任务采用同样的库 + // Poller 采集器 type Poller struct { model.Poller Device *Device reading bool - job *cron.Job + //job *cron.Job + job *gcron.Entry } // Start 启动 func (p *Poller) Start(ctx context.Context) (err error) { if p.job != nil { - p.job.Cancel() + //p.job.Cancel() + p.job.Close() //return errors.New("已经启动") } - p.job, err = cron.Interval(p.Interval, func() { - p.Execute(ctx) - }) + //p.job, err = gcron.Add(context.Background(), p.Interval, func() { + // p.Execute(ctx) + //}) + // + //p.job, err = cron.Interval(p.Interval, func() { + // p.Execute(ctx) + //}) return } @@ -50,6 +59,6 @@ func (p *Poller) read(ctx context.Context) { // Stop 结束 func (p *Poller) Stop() { if p.job != nil { - p.job.Cancel() + //p.job.Cancel() } } diff --git a/network/core/logic/baseLogic/asyncMap.go b/network/core/logic/baseLogic/asyncMap.go new file mode 100644 index 0000000..1ec3c1a --- /dev/null +++ b/network/core/logic/baseLogic/asyncMap.go @@ -0,0 +1,63 @@ +package baseLogic + +import ( + "context" + "errors" + "fmt" + "sync" + "time" +) + +type AsyncMap struct { + sync.RWMutex + info map[string]*FInfo +} + +type FInfo struct { + FuncKey string + Request interface{} + Response chan interface{} +} + +var asyncMapInfo = &AsyncMap{info: make(map[string]*FInfo)} + +func SyncRequest(ctx context.Context, id, funcKey string, params interface{}, timeout int) (interface{}, error) { + if timeout == 0 { + timeout = 45 + } + + responseChan := make(chan interface{}) + asyncMapInfo.Lock() + asyncMapInfo.info[id] = &FInfo{ + FuncKey: funcKey, + Request: params, + Response: responseChan, + } + asyncMapInfo.Unlock() + + defer func() { + asyncMapInfo.Lock() + delete(asyncMapInfo.info, id) + asyncMapInfo.Unlock() + close(responseChan) + }() + + select { + case <-ctx.Done(): + return nil, ctx.Err() + case <-time.After(time.Second * time.Duration(timeout)): + return nil, fmt.Errorf("invoke service %s timed out", funcKey) + case response := <-responseChan: + return response, nil + } +} + +func GetCallInfoById(ctx context.Context, id string) (funcKey string, params interface{}, response chan interface{}, err error) { + asyncMapInfo.RLock() + defer asyncMapInfo.RUnlock() + if info, ok := asyncMapInfo.info[id]; !ok { + return "", nil, nil, errors.New("cannot get call info by id " + id) + } else { + return info.FuncKey, info.Request, info.Response, nil + } +} diff --git a/network/core/logic/baseLogic/asyncMap_test.go b/network/core/logic/baseLogic/asyncMap_test.go new file mode 100644 index 0000000..2778b00 --- /dev/null +++ b/network/core/logic/baseLogic/asyncMap_test.go @@ -0,0 +1,64 @@ +package baseLogic + +import ( + "context" + "reflect" + "testing" + "time" +) + +// TestSyncRequest 测试 SyncRequest 函数. +func TestSyncRequest(t *testing.T) { + ctx := context.Background() + id := "testID" + funcKey := "testFunc" + params := "testParams" + timeout := 10 + + // 启动一个 goroutine 模拟异步响应. + go func() { + time.Sleep(2 * time.Second) // 模拟处理时间. + asyncMapInfo.Lock() + if info, ok := asyncMapInfo.info[id]; ok { + info.Response <- "testResponse" + } + asyncMapInfo.Unlock() + }() + + got, err := SyncRequest(ctx, id, funcKey, params, timeout) + if err != nil { + t.Errorf("SyncRequest() error = %v", err) + return + } + if got != "testResponse" { + t.Errorf("SyncRequest() got = %v, want %v", got, "testResponse") + } +} + +// TestGetCallInfoById 测试 GetCallInfoById 函数. +func TestGetCallInfoById(t *testing.T) { + ctx := context.Background() + id := "testID" + funcKey := "testFunc" + params := "testParams" + + asyncMapInfo.Lock() + asyncMapInfo.info[id] = &FInfo{ + FuncKey: funcKey, + Request: params, + Response: make(chan interface{}), + } + asyncMapInfo.Unlock() + + gotFuncKey, gotParams, _, err := GetCallInfoById(ctx, id) + if err != nil { + t.Errorf("GetCallInfoById() error = %v", err) + return + } + if gotFuncKey != funcKey { + t.Errorf("GetCallInfoById() gotFuncKey = %v, want %v", gotFuncKey, funcKey) + } + if !reflect.DeepEqual(gotParams, params) { + t.Errorf("GetCallInfoById() gotParams = %v, want %v", gotParams, params) + } +} diff --git a/network/core/logic/baseLogic/deviceAction.go b/network/core/logic/baseLogic/deviceAction.go new file mode 100644 index 0000000..ecfb897 --- /dev/null +++ b/network/core/logic/baseLogic/deviceAction.go @@ -0,0 +1,16 @@ +package baseLogic + +import ( + "context" + "sagooiot/network/model" +) + +func Online(ctx context.Context, msg model.DeviceOnlineMessage) error { + // todo 设备状态更改不再使用状态机处理更改为逻辑层超时时间控制 + return nil +} + +func Offline(ctx context.Context, msg model.DeviceOfflineMessage) error { + // todo 设备状态更改不再使用状态机处理更改为逻辑层超时时间控制 + return nil +} diff --git a/network/core/logic/baseLogic/deviceLog.go b/network/core/logic/baseLogic/deviceLog.go new file mode 100644 index 0000000..f88e843 --- /dev/null +++ b/network/core/logic/baseLogic/deviceLog.go @@ -0,0 +1,43 @@ +package baseLogic + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/queues" + "sagooiot/pkg/dcache" + "sagooiot/pkg/statistics" +) + +// InertTdLog 插入设备日志 +func InertTdLog(ctx context.Context, logType, deviceKey string, obj interface{}) { + + statistics.CountDeviceData() // 统计设备数据 + + str, strIsOk := obj.(string) + content := str + if !strIsOk { + objStr, _ := json.Marshal(obj) + content = string(objStr) + } + + var deviceLog = model.TdLogAddInput{ + Ts: gtime.Now(), + Device: deviceKey, + Type: logType, + Content: content, + } + // 向设备缓存数据库插入数据 + if err := dcache.DB().InsertData(context.Background(), deviceKey, deviceLog); err != nil { + g.Log().Debug(ctx, "Failed to insert data: %v", err) + } + data, _ := json.Marshal(deviceLog) + err := queues.DeviceDataSaveWorker.Push(ctx, consts.QueueDeviceDataSaveTopic, data, 10) + if err != nil { + g.Log().Debug(ctx, "Run TaskDeviceDataSaveWorker: %v", err) + } + +} diff --git a/network/core/logic/model/down/property/set/set.go b/network/core/logic/model/down/property/set/set.go new file mode 100644 index 0000000..07b05b4 --- /dev/null +++ b/network/core/logic/model/down/property/set/set.go @@ -0,0 +1,73 @@ +package set + +import ( + "context" + "encoding/json" + "fmt" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/iotModel/topicModel" + "strings" + "time" + + "github.com/gogf/gf/v2/util/guid" +) + +// 设备属性设置 +func PropertySet(ctx context.Context, request topicModel.TopicDownHandlerData) (map[string]interface{}, error) { + if request.DeviceDetail == nil { + return nil, fmt.Errorf("device detail is nil") + } + if request.DeviceDetail.TSL == nil { + return nil, fmt.Errorf("device tsl is nil") + } + + requestDataMap := make(map[string]interface{}) + originRequestData := make(map[string]interface{}) + if err := json.Unmarshal(request.PayLoad, &originRequestData); err != nil { + return nil, err + } + for k, v := range originRequestData { + for _, property := range request.DeviceDetail.TSL.Properties { + if property.Key == k { + requestDataMap[k] = property.ValueType.ConvertValue(v) + } + } + } + r := sagooProtocol.PropertySetRequest{ + Id: guid.S(), + Version: "1.0.0", + Params: requestDataMap, + Method: "thing.service.property.set", + } + + requestData, _ := json.Marshal(r) + if err := mqtt.Publish(fmt.Sprintf(strings.ReplaceAll(sagooProtocol.PropertySetRegisterSubRequestTopic, "+", "%s"), request.DeviceDetail.Product.Key, request.DeviceDetail.Key), requestData); err != nil { + return nil, err + } + //北向属性设置消息 + north.WriteMessage(ctx, north.PropertySetMessageTopic, nil, request.DeviceDetail.Product.Key, request.DeviceDetail.Key, iotModel.PropertySetMessage{ + Properties: requestDataMap, + Timestamp: time.Now().UnixMilli(), + }) + baseLogic.InertTdLog(ctx, consts.MsgTypePropertySet, request.DeviceDetail.Key, r) + response, err := baseLogic.SyncRequest(ctx, r.Id, "SetProperty", r, 0) + if err != nil { + return nil, err + } else if res, covertOk := response.(map[string]interface{}); !covertOk { + return nil, fmt.Errorf("set property failed,response: %+v", response) + } else { + code, _ := res["code"].(int) + //北向属性设置回复消息 + north.WriteMessage(ctx, north.PropertySetReplyMessageTopic, nil, request.DeviceDetail.Product.Key, request.DeviceDetail.Key, iotModel.PropertySetReplyMessage{ + Code: code, + Data: res, + Timestamp: time.Now().UnixMilli(), + }) + return res, nil + } +} diff --git a/network/core/logic/model/down/service/service-tunnel.go b/network/core/logic/model/down/service/service-tunnel.go new file mode 100644 index 0000000..8afbe3c --- /dev/null +++ b/network/core/logic/model/down/service/service-tunnel.go @@ -0,0 +1,46 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/network/core/server" + "sagooiot/network/core/server/base" + "sagooiot/pkg/iotModel/topicModel" + "sagooiot/pkg/plugins" + "sagooiot/pkg/plugins/model" +) + +// 往通道写数据 +func WriteTunnel(ctx context.Context, funcKey string, request topicModel.TopicDownHandlerData, paramsBytes []byte) error { + t, err := base.GetServerTunnel(ctx, request.DeviceDetail.Key) + if err != nil { + return err + } + s := server.GetServer(t.ServerId) + if s == nil { + return fmt.Errorf("server not found,serverId:%d,deviceKey:%s", t.ServerId, request.DeviceDetail.Key) + } + // 获取通道然后写数据,只管写入,不管响应,响应单独走路由 + if request.DeviceDetail.Product.MessageProtocol != consts.DefaultProtocol && request.DeviceDetail.Product.MessageProtocol != "" { + if plugins.GetProtocolPlugin() == nil { + return fmt.Errorf("protocol plugin not found") + } + // + var reqData = model.DataReq{} + reqData.Data = paramsBytes + pluginData, err := plugins.GetProtocolPlugin().GetProtocolEncodeData(request.DeviceDetail.Product.MessageProtocol, reqData) + if err != nil { + g.Log().Errorf(ctx, "get plugin error: %v, deviceKey:%s, data:%s, message ignored", err, request.DeviceDetail.Key, string(paramsBytes)) + return err + } + if pluginData.Code != 0 { + g.Log().Errorf(ctx, "plugin parse error: code:%d message:%s, deviceKey:%s, data:%s, message ignored", pluginData.Code, pluginData.Message, request.DeviceDetail.Key, string(paramsBytes)) + return fmt.Errorf("plugin parse error: code:%d message:%s, deviceKey:%s, data:%s", pluginData.Code, pluginData.Message, request.DeviceDetail.Key, string(paramsBytes)) + } + paramsBytes, _ = json.Marshal(pluginData.Data) + } + return s.Instance.GetTunnel(base.GetTunnelIdByDeviceKey(ctx, request.DeviceDetail.Key)).Write(paramsBytes) +} diff --git a/network/core/logic/model/down/service/service.go b/network/core/logic/model/down/service/service.go new file mode 100644 index 0000000..e11c4ad --- /dev/null +++ b/network/core/logic/model/down/service/service.go @@ -0,0 +1,102 @@ +package service + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/iotModel/topicModel" + "strings" + "time" + + "github.com/gogf/gf/v2/util/guid" +) + +// 服务调用 +func ServiceCall(ctx context.Context, funcKey string, request topicModel.TopicDownHandlerData) (map[string]interface{}, error) { + if request.DeviceDetail == nil { + return nil, fmt.Errorf("device detail is nil") + } + if request.DeviceDetail.TSL == nil { + return nil, fmt.Errorf("device tsl is nil") + } + requestDataMap := make(map[string]interface{}) + originRequestData := make(map[string]interface{}) + + if request.PayLoad != nil { + if err := json.Unmarshal(request.PayLoad, &originRequestData); err != nil { + return nil, err + } + } + + for _, service := range request.DeviceDetail.TSL.Functions { + if service.Key == funcKey { + for _, param := range service.Inputs { + for key, value := range originRequestData { + if key == param.Key { + requestDataMap[param.Key] = param.ValueType.ConvertValue(value) + } + } + } + } + } + r := sagooProtocol.ServiceCallRequest{ + Id: guid.S(), + Version: "1.0.0", + Params: requestDataMap, + Method: fmt.Sprintf("thing.service.%s", funcKey), + } + + requestData, _ := json.Marshal(r) + + // 记录服务下发日志 + baseLogic.InertTdLog(ctx, consts.MsgTypeFunctionSend, request.DeviceDetail.Key, r) + time.Sleep(time.Second * 1) + g.Log().Debug(ctx, "service call request: %s", string(requestData)) + + // 产品定义的传输协议支持 tcp/udp/mqtt_server/http/websocket 后面定义为变量 + if request.DeviceDetail.Product.TransportProtocol == "mqtt_server" { + if err := mqtt.Publish(fmt.Sprintf(strings.ReplaceAll(sagooProtocol.ServiceCallRegisterSubRequestTopic, "+", "%s"), request.DeviceDetail.Product.Key, request.DeviceDetail.Key, funcKey), requestData); err != nil { + return nil, err + } + } else if request.DeviceDetail.Product.TransportProtocol == "udp" || request.DeviceDetail.Product.TransportProtocol == "tcp" { + // 如果是udp或者tcp,查询出通道然后通过通道下发,需要注意的是,仅仅支持服务端建立的通道。 + //todo 暂时不支持多节点部署,支持多节点部署的话,需要一个有效的查找连接的方法 + + reqData, _ := json.Marshal(requestDataMap) + if err := WriteTunnel(ctx, funcKey, request, reqData); err != nil { + return nil, err + } + } else { + return nil, fmt.Errorf("transport protocol %s not support", request.DeviceDetail.Product.TransportProtocol) + } + //北向服务调用请求消息 + north.WriteMessage(ctx, north.ServiceCallMessageTopic, nil, request.DeviceDetail.Product.Key, request.DeviceDetail.Key, iotModel.ServiceCallMessage{ + ServiceId: funcKey, + Params: requestDataMap, + Timestamp: time.Now().UnixMilli(), + }) + response, err := baseLogic.SyncRequest(ctx, r.Id, funcKey, r, 0) + if err != nil { + return nil, err + } else if res, covertOk := response.(map[string]interface{}); !covertOk { + return nil, fmt.Errorf("invoke service %s failed,response: %+v", funcKey, response) + } else { + code, _ := res["code"].(int) + //北向服务调用响应请求消息 + north.WriteMessage(ctx, north.ServiceReplyMessageTopic, nil, request.DeviceDetail.Product.Key, request.DeviceDetail.Key, iotModel.ServiceCallReplyMessage{ + ServiceId: funcKey, + Code: code, + Data: res, + Timestamp: time.Now().UnixMilli(), + }) + return res, nil + } + +} diff --git a/network/core/logic/model/model.go b/network/core/logic/model/model.go new file mode 100644 index 0000000..d8fb548 --- /dev/null +++ b/network/core/logic/model/model.go @@ -0,0 +1,25 @@ +package model + +import ( + "context" + "sagooiot/network/core/logic/model/up/event" + "sagooiot/network/core/logic/model/up/property/batch" + "sagooiot/network/core/logic/model/up/property/reporter" + "sagooiot/network/core/logic/model/up/property/set" + "sagooiot/network/core/logic/model/up/service" +) + +func InitCoreLogic(ctx context.Context) error { + for _, v := range []func() error{ + event.Init, + batch.Init, + reporter.Init, + set.Init, + service.Init, + } { + if err := v(); err != nil { + return err + } + } + return nil +} diff --git a/network/core/logic/model/up/event/event.go b/network/core/logic/model/up/event/event.go new file mode 100644 index 0000000..5f9fa3d --- /dev/null +++ b/network/core/logic/model/up/event/event.go @@ -0,0 +1,108 @@ +package event + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/network/core" + "sagooiot/network/core/logic/model/up/property/reporter" + "sagooiot/network/core/tunnel/base" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/iotModel/topicModel" + "strings" + "time" +) + +func Init() (err error) { + // /sys/${productKey}/${devicekey}/thing/event/${tsl.event.identifier}/post + if err = core.RegisterSubTopicHandler(sagooProtocol.EventRegisterSubRequestTopic, consts.MsgTypeEvent, ReportEvent); err != nil { + return err + } + base.RegisterModelType(base.UpEvent, base.ModelType{ + LogType: consts.MsgTypeEvent, + GetTopicWithInfo: GetTopicWithInfo, + Handle: ReportEvent, + }) + return nil +} + +// 事件上报 +func ReportEvent(ctx context.Context, data topicModel.TopicHandlerData) error { + if strings.HasSuffix(data.Topic, "property/post") { + // 这段特殊的逻辑是为了处理topic通配符的问题 + return reporter.ReportProperty(ctx, data) + } + topicInfo := strings.Split(data.Topic, "/") + eventKey := topicInfo[6] + if eventKey == "property" { + // 忽略属性上报信息 + return errors.New("ignore") + } + var reportData sagooProtocol.ReportEventReq + if reportDataErr := json.Unmarshal(data.PayLoad, &reportData); reportDataErr != nil { + g.Log().Errorf(ctx, "parse data error: %v, topic:%s, message:%s, message ignored", reportDataErr, data.Topic, string(data.PayLoad)) + return reportDataErr + } + if reportData.Params.Value == nil || len(reportData.Params.Value) == 0 { + g.Log().Printf(ctx, "event data is empty, topic:%s, message:%s, message ignored\n", data.Topic, string(data.PayLoad)) + return errors.New("event data is empty") + } + var reportEventData = model.ReportEventData{ + Key: eventKey, + Param: model.ReportEventParam{ + Value: map[string]any{}, + CreateTime: reportData.Params.CreateAt, + }, + } + for _, event := range data.DeviceDetail.TSL.Events { + if event.Key == eventKey { + for _, o := range event.Outputs { + for k, v := range reportData.Params.Value { + if k == o.Name { + reportEventData.Param.Value[k] = o.ValueType.ConvertValue(v) + } + } + } + } + } + if reportEventErr := service.DevDataReport().Event(ctx, data.DeviceKey, reportEventData); reportEventErr != nil { + g.Log().Errorf(ctx, "report event error: %v, topic:%s, message:%s, message ignored", reportEventErr, data.Topic, string(data.PayLoad)) + return reportEventErr + } + //北向事件上报消息 + north.WriteMessage(ctx, north.EventReportMessageTopic, nil, data.DeviceDetail.Product.Key, data.DeviceDetail.Key, iotModel.EventReportMessage{ + EventId: eventKey, + Events: reportEventData.Param.Value, + Timestamp: time.Now().UnixMilli(), + }) + if alarmCheckErr := service.AlarmRule().Check(ctx, data.DeviceKey, data.DeviceKey, consts.AlarmTriggerTypeProperty, reportEventData); alarmCheckErr != nil { + g.Log().Errorf(ctx, "alarm check error: %v, topic:%s, message:%s, message ignored", alarmCheckErr, data.Topic, string(data.PayLoad)) + return alarmCheckErr + } + if reportData.Sys.Ack == sagooProtocol.NeedAck { + return mqtt.PublishWithInterface( + fmt.Sprintf(strings.ReplaceAll(sagooProtocol.EventRegisterPubResponseTopic, "+", "%s")+"_reply", data.ProductKey, data.DeviceKey, eventKey), + sagooProtocol.ReportEventReply{ + Code: 200, + Data: struct{}{}, + Id: reportData.Id, + Message: "success", + Method: fmt.Sprintf("thing.event.%s.post_reply", eventKey), + Version: "1.0", + }, + ) + } + return nil +} + +func GetTopicWithInfo(deviceKey, productKey, identity string) string { + return fmt.Sprintf(strings.ReplaceAll(sagooProtocol.EventRegisterSubRequestTopic, "+", "%s"), productKey, deviceKey, identity) +} diff --git a/network/core/logic/model/up/property/batch/batch.go b/network/core/logic/model/up/property/batch/batch.go new file mode 100644 index 0000000..f2bb623 --- /dev/null +++ b/network/core/logic/model/up/property/batch/batch.go @@ -0,0 +1,154 @@ +package batch + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/network/core" + "sagooiot/network/core/tunnel/base" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/iotModel/topicModel" + "strings" + "time" +) + +func Init() (err error) { + // /sys/\{productKey\}/{devicekey}/thing/event/property/pack/post + if err = core.RegisterSubTopicHandler(sagooProtocol.BatchRegisterSubRequestTopic, consts.MsgTypeGatewayBatch, GatewayBatchReportProperty); err != nil { + return err + } + base.RegisterModelType(base.UpBatch, base.ModelType{ + LogType: consts.MsgTypeGatewayBatch, + GetTopicWithInfo: GetTopicWithInfo, + Handle: GatewayBatchReportProperty, + }) + return nil +} + +// GatewayBatchReportProperty 网关批量属性上报 +func GatewayBatchReportProperty(ctx context.Context, data topicModel.TopicHandlerData) error { + var gatewayBatchReport sagooProtocol.GatewayBatchReq + if err := json.Unmarshal(data.PayLoad, &gatewayBatchReport); err != nil { + return logError(ctx, "parse data error", err, data) + } + + //网关属性处理 + if len(gatewayBatchReport.Params.Properties) > 0 { + if err := handleProperties(ctx, data, gatewayBatchReport.Params.Properties); err != nil { + return err + } + } + + //网关事件处理 + if len(gatewayBatchReport.Params.Events) > 0 { + if err := handleEvents(ctx, data, gatewayBatchReport.Params.Events, ""); err != nil { + return err + } + } + + //网关子设备处理 + for _, sub := range gatewayBatchReport.Params.SubDevices { + if len(sub.Properties) > 0 { + if err := handleProperties(ctx, data, sub.Properties); err != nil { + return err + } + } + if len(sub.Events) > 0 { + if err := handleEvents(ctx, data, sub.Events, sub.Identity.DeviceKey); err != nil { + return err + } + } + } + + //网关确认响应处理 + return handleAcknowledgment(ctx, gatewayBatchReport, data) +} + +// logError 记录错误日志并返回错误 +func logError(ctx context.Context, message string, err error, data topicModel.TopicHandlerData) error { + g.Log().Errorf(ctx, "%s: %v, topic:%s, message:%s, message ignored", message, err, data.Topic, string(data.PayLoad)) + return err +} + +// handleProperties 处理属性上报 +func handleProperties(ctx context.Context, data topicModel.TopicHandlerData, properties map[string]interface{}) error { + reportDataInfo, err := service.DevTSLParse().HandleProperties(ctx, data.DeviceDetail, properties) + if err != nil { + return logError(ctx, "parse property error", err, data) + } + + // 上报处理结果 + if len(reportDataInfo) > 0 { + //if err := service.DevDataReport().Property(ctx, data.DeviceKey, reportDataInfo, subDeviceKey); err != nil { + // return logError(ctx, "report property error", err, data) + //} + north.WriteMessage(ctx, north.PropertyReportMessageTopic, nil, data.ProductKey, data.DeviceKey, iotModel.PropertyReportMessage{ + Properties: reportDataInfo, + }) + + // 检查报警规则 + if err := service.AlarmRule().Check(ctx, data.DeviceKey, data.DeviceKey, consts.AlarmTriggerTypeProperty, reportDataInfo); err != nil { + return logError(ctx, "alarm check error", err, data) + } + } + + return nil +} + +// handleEvents 处理事件上报 +func handleEvents(ctx context.Context, data topicModel.TopicHandlerData, events map[string]sagooProtocol.EventNode, subDeviceKey string) error { + resList, err := service.DevTSLParse().HandleEvents(ctx, data.DeviceDetail, events) + if err != nil { + return logError(ctx, "parse event error", err, data) + } + + for _, e := range resList { + // 上报事件 + if len(e.Param.Value) > 0 { + //if err := service.DevDataReport().Event(ctx, data.DeviceKey, reportEventData, subDeviceKey); err != nil { + // return logError(ctx, "report event error", err, data) + //} + + north.WriteMessage(ctx, north.EventReportMessageTopic, nil, data.ProductKey, data.DeviceDetail.Key, iotModel.EventReportMessage{ + EventId: e.Key, + Events: e.Param.Value, + Timestamp: time.Now().UnixMilli(), + }) + + // 检查报警规则 + if err := service.AlarmRule().Check(ctx, data.DeviceKey, data.ProductKey, consts.AlarmTriggerTypeProperty, e); err != nil { + return logError(ctx, "alarm check error", err, data) + } + } + } + return nil +} + +// handleAcknowledgment 处理确认响应 +func handleAcknowledgment(ctx context.Context, gatewayBatchReport sagooProtocol.GatewayBatchReq, data topicModel.TopicHandlerData) error { + if gatewayBatchReport.Sys.Ack == sagooProtocol.NeedAck { + return mqtt.PublishWithInterface( + fmt.Sprintf(strings.ReplaceAll(sagooProtocol.BatchRegisterPubResponseTopic, "+", "%s"), data.ProductKey, data.DeviceKey), + sagooProtocol.GatewayBatchReply{ + Code: 200, + Data: struct{}{}, + Id: gatewayBatchReport.Id, + Message: "success", + Method: "thing.event.property.pack.post", + Version: "1.0", + }, + ) + } + return nil +} + +// GetTopicWithInfo 获取带有设备信息的主题 +func GetTopicWithInfo(productKey, deviceKey, identity string) string { + return fmt.Sprintf(strings.ReplaceAll(sagooProtocol.BatchRegisterSubRequestTopic, "+", "%s"), productKey, deviceKey) +} diff --git a/network/core/logic/model/up/property/reporter/reporter.go b/network/core/logic/model/up/property/reporter/reporter.go new file mode 100644 index 0000000..1d1cf6c --- /dev/null +++ b/network/core/logic/model/up/property/reporter/reporter.go @@ -0,0 +1,76 @@ +package reporter + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/sagooProtocol/north" + "sagooiot/pkg/iotModel/topicModel" + "strings" +) + +func Init() (err error) { + return nil +} + +// ReportProperty 属性上报 +func ReportProperty(ctx context.Context, data topicModel.TopicHandlerData) error { + // 记录开始时间 + //start := time.Now() + + //对存在转义字符的进行全量替换 + payLoad := strings.ReplaceAll(string(data.PayLoad), "\\", "") + + var reportData sagooProtocol.ReportPropertyReq + if reportDataErr := json.Unmarshal([]byte(payLoad), &reportData); reportDataErr != nil { + g.Log().Errorf(ctx, "parse data error: %s, topic:%s, message:%s, message ignored", reportDataErr, data.Topic, string(data.PayLoad)) + return reportDataErr + } + + //解析数据 + reportDataInfo, err := service.DevTSLParse().ParseData(ctx, data.DeviceKey, []byte(payLoad)) + if err != nil { + return err + } + //北向属性上报消息 + north.WriteMessage(ctx, north.PropertyReportMessageTopic, nil, data.ProductKey, data.DeviceDetail.Key, iotModel.PropertyReportMessage{ + Properties: reportDataInfo, + }) + + //告警处理 + err = service.AlarmRule().Check(ctx, data.ProductKey, data.DeviceKey, consts.AlarmTriggerTypeProperty, reportDataInfo) + if err != nil { + g.Log().Errorf(ctx, "告警检测失败: %s", err.Error()) + } + + //记录结束时间 + //end := time.Now() + //计算运行时间 + //taken := end.Sub(start) + //g.Log().Debugf(ctx, "属性上报, deviceKey:%s - ReportProperty,程序用时:%s", data.DeviceKey, taken) + if reportData.Sys.Ack == sagooProtocol.NeedAck { + return mqtt.PublishWithInterface( + fmt.Sprintf(strings.ReplaceAll(sagooProtocol.PropertyRegisterPubResponseTopic, "+", "%s"), data.ProductKey, data.DeviceKey), + sagooProtocol.ReportPropertyReply{ + Code: 200, + Data: struct{}{}, + Id: reportData.Id, + Message: "success", + Method: "thing.event.property.post", + Version: "1.0", + }, + ) + } else { + return nil + } +} + +func GetTopicWithInfo(deviceKey, productKey, identity string) string { + return fmt.Sprintf(strings.ReplaceAll(sagooProtocol.PropertyRegisterSubRequestTopic, "+", "%s"), productKey, deviceKey) +} diff --git a/network/core/logic/model/up/property/set/set.go b/network/core/logic/model/up/property/set/set.go new file mode 100644 index 0000000..0f26526 --- /dev/null +++ b/network/core/logic/model/up/property/set/set.go @@ -0,0 +1,70 @@ +package set + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/network/core" + "sagooiot/network/core/logic/baseLogic" + tunnelBase "sagooiot/network/core/tunnel/base" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/topicModel" + "strings" +) + +func Init() (err error) { + // /sys/${productKey}/${deviceKey}/thing/service/property/set_reply + if err = core.RegisterSubTopicHandler(sagooProtocol.PropertySetRegisterSubResponseTopic, consts.MsgTypePropertySetReply, PropertySet); err != nil { + return err + } + tunnelBase.RegisterModelType(tunnelBase.UpSetProperty, tunnelBase.ModelType{ + LogType: consts.MsgTypePropertySetReply, + GetTopicWithInfo: GetTopicWithInfo, + Handle: PropertySet, + }) + return nil +} + +// 属性设置 +func PropertySet(ctx context.Context, data topicModel.TopicHandlerData) error { + var propertySet sagooProtocol.PropertySetRes + if propertySetErr := json.Unmarshal(data.PayLoad, &propertySet); propertySetErr != nil { + g.Log().Debugf(ctx, "parse data error: %v, topic:%s, message:%s, message ignored", propertySetErr, data.Topic, string(data.PayLoad)) + return propertySetErr + } + var setPropertyRes sagooProtocol.PropertySetRes + if reportDataErr := json.Unmarshal(data.PayLoad, &setPropertyRes); reportDataErr != nil { + g.Log().Debugf(ctx, "parse data error: %v, topic:%s, message:%s, message ignored", reportDataErr, data.Topic, string(data.PayLoad)) + return reportDataErr + } + if setPropertyRes.Id == "" { + // 忽略Id不存在的消息 + return errors.New("ignore") + } + funcKey, _, responseChan, err := baseLogic.GetCallInfoById(ctx, setPropertyRes.Id) + if err != nil { + g.Log().Debugf(ctx, "get call info for service(id:%s) call error:%s", setPropertyRes.Id, err.Error()) + return err + } + var responseData = make(map[string]interface{}) + for _, function := range data.DeviceDetail.TSL.Functions { + if function.Key == funcKey { + for _, node := range function.Outputs { + for key, value := range setPropertyRes.Data { + if key == node.Key { + responseData[key] = node.ValueType.ConvertValue(value) + } + } + } + } + } + responseChan <- responseData + return nil +} + +func GetTopicWithInfo(deviceKey, productKey, identity string) string { + return fmt.Sprintf(strings.ReplaceAll(sagooProtocol.PropertySetRegisterSubResponseTopic, "+", "%s"), productKey, deviceKey) +} diff --git a/network/core/logic/model/up/service/service.go b/network/core/logic/model/up/service/service.go new file mode 100644 index 0000000..9542ee9 --- /dev/null +++ b/network/core/logic/model/up/service/service.go @@ -0,0 +1,71 @@ +package service + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/network/core" + "sagooiot/network/core/logic/baseLogic" + tunnelBase "sagooiot/network/core/tunnel/base" + "sagooiot/pkg/iotModel/sagooProtocol" + "sagooiot/pkg/iotModel/topicModel" + "strings" +) + +func Init() (err error) { + // /sys/${productKey}/${devicekey}/thing/service/${tsl.service.identifier}_reply + if err = core.RegisterSubTopicHandler("/sys/+/+/thing/service/+", consts.MsgTypeFunctionReply, ServiceCallOutput); err != nil { + return err + } + tunnelBase.RegisterModelType(tunnelBase.UpServiceOutput, tunnelBase.ModelType{ + LogType: consts.MsgTypeFunctionReply, + GetTopicWithInfo: GetTopicWithInfo, + Handle: ServiceCallOutput, + }) + return nil +} + +// 服务调用 +func ServiceCallOutput(ctx context.Context, data topicModel.TopicHandlerData) error { + topicInfo := strings.Split(data.Topic, "/") + if !strings.HasSuffix(topicInfo[6], "reply") { + // 忽略非服务调用 + return errors.New("ignore") + } + var serviceCallResult sagooProtocol.ServiceCallOutputRes + if reportDataErr := json.Unmarshal(data.PayLoad, &serviceCallResult); reportDataErr != nil { + g.Log().Errorf(ctx, "parse data error: %v, topic:%s, message:%s, message ignored", reportDataErr, data.Topic, string(data.PayLoad)) + return reportDataErr + } + if serviceCallResult.Id == "" { + // 忽略Id不存在的消息 + return errors.New("ignore") + } + funcKey, _, responseChan, err := baseLogic.GetCallInfoById(ctx, serviceCallResult.Id) + if err != nil { + g.Log().Errorf(ctx, "get call info for service(id:%s) call error:%s", serviceCallResult.Id, err.Error()) + return err + } + var responseData = make(map[string]interface{}) + for _, function := range data.DeviceDetail.TSL.Functions { + if function.Key == funcKey { + for _, node := range function.Outputs { + for key, value := range serviceCallResult.Data { + if key == node.Key { + responseData[key] = node.ValueType.ConvertValue(value) + } + } + } + } + } + responseChan <- responseData + return nil +} + +func GetTopicWithInfo(productKey, deviceKey, identity string) string { + str := strings.ReplaceAll(sagooProtocol.ServiceCallRegisterSubResponseTopic, "+", "%s") + "_reply" + return fmt.Sprintf(str, productKey, deviceKey, identity) +} diff --git a/network/core/common.go b/network/core/mapper/common.go similarity index 96% rename from network/core/common.go rename to network/core/mapper/common.go index 9b00b66..21fa074 100644 --- a/network/core/common.go +++ b/network/core/mapper/common.go @@ -1,4 +1,4 @@ -package core +package mapper import ( "context" diff --git a/network/core/mapper.go b/network/core/mapper/mapper.go similarity index 68% rename from network/core/mapper.go rename to network/core/mapper/mapper.go index 062d8d9..15f8750 100644 --- a/network/core/mapper.go +++ b/network/core/mapper/mapper.go @@ -1,15 +1,15 @@ -package core +package mapper import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - logicModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/network/model" + "sagooiot/internal/consts" + logicModel "sagooiot/internal/model" + "sagooiot/network/model" "strconv" ) -// todo 需要处理下心跳和注册包,需要考虑到客户端可能不支持心跳或者注册包 -func MapperServer(ctx context.Context, res logicModel.NetworkServerOut) model.Server { +// Server Server todo 需要处理下心跳和注册包,需要考虑到客户端可能不支持心跳或者注册包 +func Server(ctx context.Context, res *logicModel.NetworkServerOut) model.Server { s := model.Server{ Id: res.Id, Name: res.Name, @@ -19,9 +19,16 @@ func MapperServer(ctx context.Context, res logicModel.NetworkServerOut) model.Se Heartbeat: model.HeartBeatPacket{}, Protocol: model.Protocol{}, //TODO 这里暂时不写device,需要等待后续的device插入数据,考虑是不是启动的时候带入 - //Devices: res.Devices, + //Devices: res.Devices, Disabled: true, - Created: res.CreatedAt.Time, + //Created: res.CreatedAt.Time, + IsTls: res.IsTls, + AuthType: res.AuthType, + AuthUser: res.AuthUser, + AuthPasswd: res.AuthPasswd, + AccessToken: res.AccessToken, + CertificateId: res.CertificateId, + Stick: res.Stick, } //TODO 等待model和前端修改补充下mqtt的options,主要是额外的一些配置 if res.Status == consts.ServerStatusOnline { @@ -33,12 +40,12 @@ func MapperServer(ctx context.Context, res logicModel.NetworkServerOut) model.Se return s } -func MapperDevice(res logicModel.DeviceOutput) model.Device { +func Device(res logicModel.DeviceOutput) model.Device { return model.Device{ - Id: uint64(res.Id), - TunnelId: uint64(res.TunnelId), - ProductId: strconv.Itoa(int(res.ProductId)), - Name: res.Name, + DeviceKey: res.Key, + TunnelId: uint64(res.TunnelId), + ProductKey: res.ProductKey, + Name: res.Name, //todo 这里的station等待后面处理 Station: res.Status, Disabled: false, @@ -46,7 +53,7 @@ func MapperDevice(res logicModel.DeviceOutput) model.Device { } } -func mapperProduct(res logicModel.DetailProductOutput) model.Product { +func Product(res logicModel.DetailProductOutput) model.Product { return model.Product{ Id: strconv.Itoa(int(res.Id)), Name: res.Name, @@ -64,7 +71,7 @@ func mapperProduct(res logicModel.DetailProductOutput) model.Product { } } -func mapperTunnel(ctx context.Context, res logicModel.NetworkTunnelOut) model.Tunnel { +func Tunnel(ctx context.Context, res logicModel.NetworkTunnelOut) model.Tunnel { t := model.Tunnel{ Id: uint64(res.Id), ServerId: res.ServerId, diff --git a/network/core/mqtt-databus.go b/network/core/mqtt-databus.go deleted file mode 100644 index 61b2c7f..0000000 --- a/network/core/mqtt-databus.go +++ /dev/null @@ -1,71 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - networkModel "github.com/sagoo-cloud/sagooiot/network/model" - "strings" - - MQTT "github.com/eclipse/paho.mqtt.golang" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" -) - -func onlineHandler(ctx context.Context, client MQTT.Client, message MQTT.Message) { - msg := networkModel.DeviceOnlineMessage{} - if err := json.Unmarshal(message.Payload(), &msg); err != nil { - g.Log().Errorf(ctx, "onlineHandler error: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } - if err := service.TdLogTable().Insert(ctx, &model.TdLogAddInput{ - Ts: gtime.Now(), - Device: msg.DeviceKey, - Type: consts.GetTopicType(consts.DataBusOnline), - Content: string(message.Payload()), - }); err != nil { - g.Log().Errorf(ctx, "insert deviceLog failed, err: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } - if err := service.DevDevice().Online(ctx, msg.DeviceKey); err != nil { - g.Log().Errorf(ctx, "onlineHandler set device online error: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } -} - -func offlineHandler(ctx context.Context, client MQTT.Client, message MQTT.Message) { - msg := networkModel.DeviceOfflineMessage{} - if err := json.Unmarshal(message.Payload(), &msg); err != nil { - g.Log().Errorf(ctx, "offlineHandler error: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } - if err := service.TdLogTable().Insert(ctx, &model.TdLogAddInput{ - Ts: gtime.Now(), - Device: msg.DeviceKey, - Type: consts.GetTopicType(consts.DataBusOffline), - Content: string(message.Payload()), - }); err != nil { - g.Log().Errorf(ctx, "insert deviceLog failed, err: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } - if err := service.DevDevice().Offline(ctx, msg.DeviceKey); err != nil { - g.Log().Errorf(ctx, "onlineHandler set device online error: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - } -} - -func reportPropertiesHandler(ctx context.Context, client MQTT.Client, message MQTT.Message) { - var reportMessage networkModel.ReportPropertyMessage - if err := json.Unmarshal(message.Payload(), &reportMessage); err != nil { - g.Log().Errorf(ctx, "unmarshal error: %w, topic:%s, message:%s, message ignored", err, message.Topic(), string(message.Payload())) - return - } - if insertErr := service.TSLTable().Insert(ctx, reportMessage.DeviceKey, reportMessage.Properties); insertErr != nil { - g.Log().Errorf(ctx, "insert error: %w, topic:%s, message:%s, message ignored", insertErr, message.Topic(), string(message.Payload())) - return - } - topicNodes := strings.Split(message.Topic(), "/") - if len(topicNodes) < 3 { - return - } - if alarmCheckErr := service.AlarmRule().Check(ctx, topicNodes[2], reportMessage.DeviceKey, model.AlarmTriggerTypeProperty, reportMessage.Properties); alarmCheckErr != nil { - g.Log().Errorf(ctx, "alarm check error: %w, topic:%s, message:%s, message ignored", alarmCheckErr, message.Topic(), string(message.Payload())) - return - } -} diff --git a/network/core/mqtt-device-property-report.go b/network/core/mqtt-device-property-report.go deleted file mode 100644 index 2c2a0dd..0000000 --- a/network/core/mqtt-device-property-report.go +++ /dev/null @@ -1,58 +0,0 @@ -package core - -import ( - "encoding/json" - "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gtime" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - networkModel "github.com/sagoo-cloud/sagooiot/network/model" - "time" -) - -type MessagePropertyReporter struct { - r messageRouter -} - -func (m MessagePropertyReporter) DeviceDataHandle() { - if err := service.TdLogTable().Insert(m.r.ctx, &model.TdLogAddInput{ - Ts: gtime.Now(), - Device: m.r.deviceDetail.Key, - Type: consts.GetTopicType(consts.DataBusPropertyReport), - Content: string(m.r.msg), - }); err != nil { - g.Log().Errorf(m.r.ctx, "insert deviceLog failed, err: %w, message:%s, message ignored", err, string(m.r.msg)) - } - - if m.r.data == nil || len(m.r.data) == 0 { - g.Log().Printf(m.r.ctx, "report data is empty, message:%s, message ignored\n", string(m.r.msg)) - return - } - - var reportFormatData = make(map[string]interface{}) - for k, v := range m.r.data { - for _, property := range m.r.deviceDetail.TSL.Properties { - if property.Key == k { - reportFormatData[k] = property.ValueType.ConvertValue(v) - break - } - } - } - nowTime := time.Now().Unix() - reportMessage := networkModel.ReportPropertyMessage{ - Common: networkModel.Common{ - DeviceKey: m.r.deviceDetail.Key, - MessageId: fmt.Sprintf("%s-%s-%d", m.r.deviceDetail.Product.Key, m.r.deviceDetail.Key, nowTime), - Timestamp: nowTime, - }, - Properties: reportFormatData, - } - reportFormatDataByte, _ := json.Marshal(reportMessage) - if propertyReportErr := mqtt.Publish(consts.GetDataBusWrapperTopic(m.r.deviceDetail.Product.Key, m.r.deviceDetail.Key, consts.DataBusPropertyReport), reportFormatDataByte); propertyReportErr != nil { - g.Log().Errorf(m.r.ctx, "publish data error: %w,message:%s, message ignored", propertyReportErr, string(m.r.msg)) - return - } -} diff --git a/network/core/mqtt-device-router.go b/network/core/mqtt-device-router.go deleted file mode 100644 index 2c63b8f..0000000 --- a/network/core/mqtt-device-router.go +++ /dev/null @@ -1,34 +0,0 @@ -package core - -import ( - "context" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/model" - - "github.com/gogf/gf/v2/frame/g" -) - -type messageRouterHandler interface { - DeviceDataHandle() -} - -type messageRouter struct { - ctx context.Context - msg []byte - data map[string]interface{} - msgType string - deviceDetail *model.DeviceOutput -} - -func (m messageRouter) router() { - var h messageRouterHandler - switch m.msgType { - case consts.PropertyReport: - h = MessagePropertyReporter{m} - } - if h == nil { - g.Log().Errorf(m.ctx, "message type:%s not supported, message:%s, message ignored", m.msgType, string(m.msg)) - return - } - h.DeviceDataHandle() -} diff --git a/network/core/mqtt-device.go b/network/core/mqtt-device.go deleted file mode 100644 index c7e0c02..0000000 --- a/network/core/mqtt-device.go +++ /dev/null @@ -1,97 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "fmt" - MQTT "github.com/eclipse/paho.mqtt.golang" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/extend" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - networkModel "github.com/sagoo-cloud/sagooiot/network/model" - "strings" - "time" -) - -var subMap = map[consts.Topic]func(context.Context, MQTT.Client, MQTT.Message){ - //device 处理的topic - consts.TopicDeviceData: deviceDataHandler, - - //dataBus 处理的topic - consts.CommonDataBusPrefix + consts.DataBusOnline: onlineHandler, - consts.CommonDataBusPrefix + consts.DataBusOffline: offlineHandler, - consts.CommonDataBusPrefix + consts.DataBusPropertyReport: reportPropertiesHandler, -} - -func StartSubscriber(ctx context.Context) error { - for topic, f := range subMap { - if err := mqtt.Subscribe(ctx, string(topic), f); err != nil { - return err - } - } - return nil -} - -func deviceDataHandler(ctx context.Context, client MQTT.Client, message MQTT.Message) { - topicInfo := strings.Split(message.Topic(), "/") - if len(topicInfo) != 3 { - g.Log().Error(ctx, fmt.Sprintf("topic:%s is illegal, message(%s) ignored", message.Topic(), string(message.Payload()))) - return - } - productKey, deviceKey := topicInfo[1], topicInfo[2] - productDetail, productDetailErr := service.DevProduct().Get(ctx, productKey) - if productDetailErr != nil || productDetail == nil { - g.Log().Errorf(ctx, "find product info error: %w, topic:%s, message:%s, message ignored", productDetailErr, message.Topic(), string(message.Payload())) - return - } - deviceDetail, err := service.DevDevice().Get(ctx, deviceKey) - if err != nil { - return - } - if deviceDetail == nil { - g.Log().Errorf(ctx, "fail to find device, topic:%s, message:%s, message ignored", message.Topic(), string(message.Payload())) - return - } - if deviceDetail != nil && deviceDetail.Status != consts.DeviceStatueOnline { - onlineMessage, _ := json.Marshal(networkModel.DeviceOnlineMessage{ - DeviceKey: deviceKey, - Timestamp: time.Now().Unix(), - }) - if propertyReportErr := mqtt.Publish(consts.GetDataBusWrapperTopic(productKey, deviceKey, consts.DataBusOnline), onlineMessage); propertyReportErr != nil { - g.Log().Errorf(ctx, "publish formate data error: %w, topic:%s, message:%s, message ignored", propertyReportErr, message.Topic(), string(message.Payload())) - return - } - } - messageProtocol := productDetail.MessageProtocol - res := string(message.Payload()) - if messageProtocol != consts.DefaultProtocol && messageProtocol != "" { - if extend.GetProtocolPlugin() == nil { - return - } - pluginData, err := extend.GetProtocolPlugin().GetProtocolUnpackData(messageProtocol, message.Payload()) - if err != nil { - g.Log().Errorf(ctx, "get plugin error: %w, deviceKey:%s, data:%s, message ignored", err, deviceDetail.Key, string(message.Payload())) - return - } - if pluginData.Code != 0 { - g.Log().Errorf(ctx, "plugin parse error: code:%d message:%s, deviceKey:%s, data:%s, message ignored", pluginData.Code, pluginData.Message, deviceDetail.Key, string(message.Payload())) - return - } - pluginDataByte, _ := json.Marshal(pluginData.Data) - res = string(pluginDataByte) - } - var reportData networkModel.DefaultMessageType - if reportDataErr := json.Unmarshal([]byte(res), &reportData); reportDataErr != nil { - g.Log().Errorf(ctx, "parse data error: %w, topic:%s, message:%s, message ignored", reportDataErr, message.Topic(), string(message.Payload())) - return - } - messageRouter{ - ctx: ctx, - data: reportData.Data, - msgType: reportData.DataType, - deviceDetail: deviceDetail, - msg: message.Payload(), - }.router() -} diff --git a/network/core/point.go b/network/core/point.go deleted file mode 100644 index 85c8e6c..0000000 --- a/network/core/point.go +++ /dev/null @@ -1,9 +0,0 @@ -package core - -import ( - "github.com/sagoo-cloud/sagooiot/network/model" -) - -type Point struct { - model.Point -} diff --git a/network/core/product-manager.go b/network/core/product-manager.go index 5afa548..8faa512 100644 --- a/network/core/product-manager.go +++ b/network/core/product-manager.go @@ -2,33 +2,29 @@ package core import ( "context" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" - "strconv" + "sagooiot/internal/service" + "sagooiot/network/core/mapper" + "sagooiot/network/model" "sync" ) var products sync.Map -func LoadProduct(ctx context.Context, id string) (*model.Product, error) { - v, ok := products.Load(id) +func LoadProduct(ctx context.Context, key string) (*model.Product, error) { + v, ok := products.Load(key) if ok { return v.(*model.Product), nil } //加载产品 var product model.Product - productId, err := strconv.Atoi(id) + productDetail, err := service.DevProduct().Detail(ctx, key) if err != nil { return nil, err } - productDetail, err := service.DevProduct().Detail(ctx, uint(productId)) - if err != nil { - return nil, err - } - product = mapperProduct(*productDetail) + product = mapper.Product(*productDetail) - products.Store(id, &product) + products.Store(key, &product) return &product, nil } diff --git a/network/core/router.go b/network/core/router.go new file mode 100644 index 0000000..2ee92f9 --- /dev/null +++ b/network/core/router.go @@ -0,0 +1,178 @@ +package core + +import ( + "context" + "encoding/json" + "errors" + "fmt" + MQTT "github.com/eclipse/paho.mqtt.golang" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/mqtt" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/pkg/dcache" + "sagooiot/pkg/gpool" + "sagooiot/pkg/iotModel/topicModel" + "sagooiot/pkg/jsinterpreter" + "sagooiot/pkg/plugins" + "strings" + "sync" +) + +type filterMsgFunc struct { + name string + f func(message MQTT.Message) MQTT.Message +} + +type ( + SubMap struct { + sync.RWMutex + // 订阅主题 + subTopics map[string]handleFunc + // 订阅处理前filter + subTopicBeforeHandlerChain []filterMsgFunc + // 订阅处理后filter + subTopicAfterHandlerChain []filterMsgFunc + } + handleFunc struct { + logType string + f func(context.Context, topicModel.TopicHandlerData) error + } +) + +var subMapInfo = SubMap{ + subTopics: make(map[string]handleFunc), + subTopicBeforeHandlerChain: make([]filterMsgFunc, 0), + subTopicAfterHandlerChain: make([]filterMsgFunc, 0), +} + +// 控制携程数量 +var gPool = gpool.NewGPool(10000) + +// HandleMessage 所有订阅的入口 +func (s *SubMap) HandleMessage(ctx context.Context, handleF handleFunc) func(context.Context, MQTT.Client, MQTT.Message) { + return func(ctx context.Context, client MQTT.Client, message MQTT.Message) { + gPool.Go(func(ctx context.Context) error { + // 根据topic拿到deviceKey和productKey + topicInfo := strings.Split(message.Topic(), "/") + if len(topicInfo) < 3 { + //todo 是否入库,前端展示 + return errors.New(fmt.Sprintf("topic:%s is illegal, message(%s) ignored", message.Topic(), string(message.Payload()))) + } + if topicInfo[1] != "sys" && topicInfo[1] != "ota" { + //todo 非sys开头的topic不处理 + return errors.New(fmt.Sprintf("topic:%s is not supported,message(%s) ignored", message.Topic(), string(message.Payload()))) + } + productKey, deviceKey := topicInfo[2], topicInfo[3] + if topicInfo[1] == "ota" { + productKey, deviceKey = topicInfo[4], topicInfo[5] + } + res := string(message.Payload()) + + logType := handleF.logType + if len(topicInfo) == 8 && topicInfo[6] == "property" { + logType = consts.MsgTypePropertyReport + } + // 忽略一些不需要处理的消息 + if len(topicInfo) == 8 && logType == consts.MsgTypeFunctionReply && !strings.HasSuffix(topicInfo[6], "reply") { + g.Log().Infof(ctx, "handleF: topic:%s, message:%s, message ignored", message.Topic(), string(message.Payload())) + return nil + } + + // 获取设备详情,拿出来消息协议,然后按照产品定义的消息协议解析消息 + deviceInfo, err := dcache.GetDeviceDetailInfo(deviceKey) + if err != nil { + g.Log().Debugf(ctx, "device info error: %v, topic:%s, message:%s, message ignored", err.Error(), message.Topic(), string(message.Payload())) + return nil + } + if deviceInfo == nil { + g.Log().Debugf(ctx, "device info is nil, topic:%s, message:%s, message ignored", message.Topic(), string(message.Payload())) + return nil + } + + // 设备禁用不处理 + if deviceInfo.Status == model.DeviceStatusNoEnable { + g.Log().Debug(ctx, deviceKey, "device is no enable") + return nil + } + + dcache.UpdateStatus(ctx, deviceInfo) //更新设备状态 + + messageProtocol := deviceInfo.Product.MessageProtocol + if messageProtocol != consts.DefaultProtocol && messageProtocol != "" { + if plugins.GetProtocolPlugin() == nil { + return nil + } + pluginData, err := plugins.GetProtocolPlugin().GetProtocolDecodeData(deviceInfo.Product.MessageProtocol, message.Payload()) + if err != nil { + return errors.New(fmt.Sprintf("get plugin error: %v, deviceKey:%s, data:%s, message ignored", err, deviceKey, string(message.Payload()))) + + } + if pluginData.Code != 0 { + return errors.New(fmt.Sprintf("plugin parse error: code:%d message:%s, deviceKey:%s, data:%s, message ignored", pluginData.Code, pluginData.Message, deviceKey, string(message.Payload()))) + + } + pluginDataByte, _ := json.Marshal(pluginData.Data) + res = string(pluginDataByte) + } + + // 如果有js脚本,根据js脚本处理解析后的数据,处理后的数据数据格式为默认的消息协议格式 + if deviceInfo.Product.ScriptInfo != "" { + var runScriptErr error + res, runScriptErr = jsinterpreter.RunScript(res, deviceInfo.Product.ScriptInfo) + if runScriptErr != nil { + return errors.New(fmt.Sprintf("runScriptErr error: %v, topic:%s, message:%s, message ignored", runScriptErr, message.Topic(), string(message.Payload()))) + } + } + + go baseLogic.InertTdLog(ctx, logType, deviceKey, res) + + // 前置处理器处理 + for _, filter := range s.subTopicBeforeHandlerChain { + message = filter.f(message) + } + // 真正的topic处理方法 + if err := handleF.f(ctx, topicModel.TopicHandlerData{ + Topic: message.Topic(), + ProductKey: productKey, + DeviceKey: deviceKey, + PayLoad: []byte(res), + DeviceDetail: deviceInfo, + }); err != nil { + if err.Error() != "ignore" { + return errors.New(fmt.Sprintf("handleF error: %s, topic:%s, message:%s ", err.Error(), message.Topic(), string(message.Payload()))) + + } else { + g.Log().Infof(ctx, "handleF: %s, topic:%s, message:%s, message ignored", err.Error(), message.Topic(), string(message.Payload())) + return nil + } + } + + // 后置处理器处理 + for _, filter := range s.subTopicAfterHandlerChain { + message = filter.f(message) + } + return nil + }) + } +} + +func RegisterSubTopicHandler(topic, logType string, handler func(context.Context, topicModel.TopicHandlerData) error) error { + subMapInfo.Lock() + defer subMapInfo.Unlock() + if _, ok := subMapInfo.subTopics[topic]; ok { + return fmt.Errorf("topic %s already registered", topic) + } + subMapInfo.subTopics[topic] = handleFunc{f: handler, logType: logType} + return nil +} + +func StartSubscriber(ctx context.Context) error { + for topic := range subMapInfo.subTopics { + if err := mqtt.Subscribe(ctx, topic, subMapInfo.HandleMessage(ctx, subMapInfo.subTopics[topic])); err != nil { + return err + } + } + return nil +} diff --git a/network/core/server-common.go b/network/core/server-common.go deleted file mode 100644 index bd3957f..0000000 --- a/network/core/server-common.go +++ /dev/null @@ -1,46 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" - "time" - - "github.com/gogf/gf/v2/frame/g" -) - -func ServerOpenAction(serverId int) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionOpen, serverId), nil) -} - -func ServerCloseAction(serverId int) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionClose, serverId), nil) -} - -func ServerTunnelAction(ctx context.Context, serverId int, deviceKey string) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionTunnel, serverId), nil) - deviceDetail, _ := service.DevDevice().Get(ctx, deviceKey) - if deviceDetail == nil { - g.Log().Errorf(ctx, "deviceKey:%s not found,ignore", deviceKey) - return - } - _, err := LoadDevice(ctx, deviceDetail.Id) - if err != nil { - g.Log().Error(ctx, err) - } - if deviceDetail != nil && deviceDetail.Status != consts.DeviceStatueOnline { - onlineMessage, _ := json.Marshal(model.DeviceOnlineMessage{ - DeviceKey: deviceKey, - Timestamp: time.Now().Unix(), - }) - if dataBusOnlineErr := mqtt.Publish(consts.GetDataBusWrapperTopic(deviceDetail.Product.Key, deviceKey, consts.DataBusOnline), onlineMessage); dataBusOnlineErr != nil { - g.Log().Errorf(ctx, "publish data error: %w, topic:%s, message:%s, message ignored", dataBusOnlineErr, - consts.GetDataBusWrapperTopic(deviceDetail.Product.Key, deviceKey, consts.DataBusOnline), - string(onlineMessage)) - return - } - } -} diff --git a/network/core/server-tcp-tunnel.go b/network/core/server-tcp-tunnel.go deleted file mode 100644 index 6892acb..0000000 --- a/network/core/server-tcp-tunnel.go +++ /dev/null @@ -1,58 +0,0 @@ -package core - -import ( - "context" - "errors" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "net" -) - -// ServerTcpTunnel 网络连接 -type ServerTcpTunnel struct { - serverId int - deviceKey string - tunnelBase -} - -func newServerTcpTunnel(serverId, tunnelId int, deviceKey string, conn net.Conn) *ServerTcpTunnel { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOnline, tunnelId), nil) - return &ServerTcpTunnel{serverId: serverId, deviceKey: deviceKey, tunnelBase: tunnelBase{ - tunnelId: tunnelId, - link: conn, - }} -} - -func (l *ServerTcpTunnel) Open(ctx context.Context) error { - return errors.New("ServerTcpTunnel cannot open") -} - -func (l *ServerTcpTunnel) receive(ctx context.Context) { - l.running = true - l.online = true - TunnelOnlineAction(ctx, l.tunnelId, l.deviceKey) - - buf := make([]byte, 1024) - for { - n, err := l.link.Read(buf) - if err != nil { - l.onClose() - break - } - if n == 0 { - continue - } - if l.pipe != nil { - _, err = l.pipe.Write(buf[:n]) - if err != nil { - l.pipe = nil - } else { - continue - } - } - go l.tunnelBase.ReadData(ctx, l.deviceKey, buf[:n]) - } - l.running = false - l.online = false - TunnelOfflineAction(ctx, l.serverId, l.tunnelId) -} diff --git a/network/core/server-tcp.go b/network/core/server-tcp.go deleted file mode 100644 index 1689c67..0000000 --- a/network/core/server-tcp.go +++ /dev/null @@ -1,149 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "errors" - "github.com/sagoo-cloud/sagooiot/internal/consts" - interlModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/core/tunnelinstance" - "github.com/sagoo-cloud/sagooiot/network/model" - "net" -) - -type ServerTCP struct { - server *model.Server - - children map[int]*ServerTcpTunnel - - listener *net.TCPListener - - running bool -} - -func newServerTCP(server *model.Server) *ServerTCP { - svr := &ServerTCP{ - server: server, - children: make(map[int]*ServerTcpTunnel), - } - return svr -} - -func (server *ServerTCP) Open(ctx context.Context) error { - if server.running { - return errors.New("server is opened") - } - ServerOpenAction(server.server.Id) - - addr, err := net.ResolveTCPAddr("tcp", resolvePort(server.server.Addr)) - if err != nil { - return err - } - server.listener, err = net.ListenTCP("tcp", addr) - if err != nil { - return err - } - - server.running = true - go func() { - for { - c, err := server.listener.AcceptTCP() - if err != nil { - //todo print err - break - } - - buf := make([]byte, 128) - n := 0 - n, err = c.Read(buf) - if err != nil { - _ = c.Close() - continue - } - data := buf[:n] - deviceKey, checkIsOk := server.server.Register.Check(data) - if !checkIsOk { - _ = c.Close() - continue - } - _, tunnelList, err := service.NetworkTunnel().GetTunnelList(ctx, &interlModel.GetNetworkTunnelListInput{ - ServiceId: server.server.Id, - DeviceKey: deviceKey, - }) - if err != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(err.Error())) - continue - } - tunnelId := 0 - if len(tunnelList) == 0 { - heartbeatData, _ := json.Marshal(server.server.Heartbeat) - var addTunnelError error - if tunnelId, addTunnelError = service.NetworkTunnel().AddTunnel(ctx, interlModel.NetworkTunnelAddInput{ - ServerId: server.server.Id, - Name: deviceKey, - Types: "server-tcp", - Addr: c.LocalAddr().String(), - Remote: c.RemoteAddr().String(), - Heartbeat: string(heartbeatData), - Protoccol: "", - Status: 0, - Remark: "", - }); addTunnelError != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(addTunnelError.Error())) - continue - } - } else { - tunnelId = tunnelList[0].Id - if editTunnelError := service.NetworkTunnel().EditTunnel(ctx, interlModel.NetworkTunnelEditInput{ - Id: tunnelList[0].Id, - NetworkTunnelAddInput: interlModel.NetworkTunnelAddInput{ - ServerId: tunnelList[0].ServerId, - Name: tunnelList[0].Name, - Types: tunnelList[0].Types, - Addr: c.LocalAddr().String(), - Remote: c.RemoteAddr().String(), - Retry: tunnelList[0].Retry, - Heartbeat: tunnelList[0].Heartbeat, - Serial: tunnelList[0].Serial, - Protoccol: tunnelList[0].Protoccol, - Status: tunnelList[0].Status, - Remark: tunnelList[0].Remark, - }, - }); editTunnelError != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(editTunnelError.Error())) - continue - } - } - tnl := newServerTcpTunnel(server.server.Id, tunnelId, deviceKey, c) - go tnl.receive(ctx) - server.children[tunnelId] = tnl - ServerTunnelAction(ctx, server.server.Id, deviceKey) - } - server.running = false - }() - return nil -} - -func (server *ServerTCP) Close() (err error) { - ServerCloseAction(server.server.Id) - if server.children != nil { - for _, l := range server.children { - _ = l.Close() - } - } - return server.listener.Close() -} - -func (server *ServerTCP) GetTunnel(id int) tunnelinstance.TunnelInstance { - return server.children[id] -} - -func (server *ServerTCP) RemoveTunnel(id int) { - delete(server.children, id) -} - -func (server *ServerTCP) Running() bool { - return server.running -} diff --git a/network/core/server-udp-tunnel.go b/network/core/server-udp-tunnel.go deleted file mode 100644 index 84926a2..0000000 --- a/network/core/server-udp-tunnel.go +++ /dev/null @@ -1,116 +0,0 @@ -package core - -import ( - "context" - "errors" - "io" - "net" - "time" -) - -// ServerUdpTunnel UDP链接 -type ServerUdpTunnel struct { - tunnelBase - deviceKey string - conn *net.UDPConn - addr *net.UDPAddr -} - -func newServerUdpTunnel(deviceKey string, tunnelId int, conn *net.UDPConn, addr *net.UDPAddr) *ServerUdpTunnel { - return &ServerUdpTunnel{ - tunnelBase: tunnelBase{ - tunnelId: tunnelId, - link: conn, - }, - deviceKey: deviceKey, - conn: conn, - addr: addr, - } -} - -func (l *ServerUdpTunnel) Open(ctx context.Context) error { - return errors.New("ServerUdpTunnel cannot open") -} - -func (l *ServerUdpTunnel) Close() error { - return errors.New("ServerUdpTunnel cannot close") -} - -// Write 写 -func (l *ServerUdpTunnel) Write(data []byte) error { - if !l.running { - return errors.New("tunnel closed") - } - if l.pipe != nil { - return nil //透传模式下,直接抛弃 - } - _, err := l.conn.WriteToUDP(data, l.addr) - return err -} - -func (l *ServerUdpTunnel) Ask(cmd []byte, timeout time.Duration) ([]byte, error) { - if !l.running { - return nil, errors.New("tunnel closed") - } - //堵塞 - l.lock.Lock() - defer l.lock.Unlock() //自动解锁 - - err := l.Write(cmd) - if err != nil { - return nil, err - } - return l.wait(timeout) -} - -func (l *ServerUdpTunnel) Pipe(pipe io.ReadWriteCloser) { - //关闭之前的透传 - if l.pipe != nil { - _ = l.pipe.Close() - } - l.pipe = pipe - - //传入空,则关闭 - if l.pipe == nil { - return - } - - go func() { - buf := make([]byte, 1024) - for { - n, err := pipe.Read(buf) - if err != nil { - //if err == io.EOF { - // continue - //} - //pipe关闭,则不再透传 - break - } - //将收到的数据转发出去 - //n, err = l.link.Write(buf[:n]) - _, err = l.conn.WriteToUDP(buf[:n], l.addr) - if err != nil { - //发送失败,说明连接失效 - _ = pipe.Close() - break - } - } - l.pipe = nil - }() -} - -func (l *ServerUdpTunnel) onData(ctx context.Context, data []byte) { - l.running = true - l.online = true - - //透传 - if l.pipe != nil { - _, err := l.pipe.Write(data) - if err == nil { - return - } - l.pipe = nil - } - - go l.tunnelBase.ReadData(ctx, l.deviceKey, data) -} diff --git a/network/core/server-udp.go b/network/core/server-udp.go deleted file mode 100644 index 9fbea5f..0000000 --- a/network/core/server-udp.go +++ /dev/null @@ -1,163 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "errors" - "github.com/sagoo-cloud/sagooiot/internal/consts" - interlModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/core/tunnelinstance" - "github.com/sagoo-cloud/sagooiot/network/model" - "net" -) - -// ServerUDP UDP服务器 -type ServerUDP struct { - server *model.Server - - children map[int]*ServerUdpTunnel - tunnels map[string]*ServerUdpTunnel - - listener *net.UDPConn - running bool -} - -func newServerUDP(server *model.Server) *ServerUDP { - svr := &ServerUDP{ - server: server, - children: make(map[int]*ServerUdpTunnel), - tunnels: make(map[string]*ServerUdpTunnel), - } - return svr -} - -// Open 打开 -func (server *ServerUDP) Open(ctx context.Context) error { - if server.running { - return errors.New("server is opened") - } - ServerOpenAction(server.server.Id) - - addr, err := net.ResolveUDPAddr("udp", resolvePort(server.server.Addr)) - if err != nil { - return err - } - c, err := net.ListenUDP("udp", addr) - if err != nil { - //TODO 需要正确处理接收错误 - return err - } - server.listener = c //共用连接 - - server.running = true - go func() { - for { - buf := make([]byte, 1024) - n, addr, err := c.ReadFromUDP(buf) - if err != nil { - _ = c.Close() - //continue - break - } - data := buf[:n] - - //如果已经保存了链接 TODO 要有超时处理 - tnl, ok := server.tunnels[addr.String()] - if ok { - tnl.onData(ctx, data) - continue - } - - deviceKey, checkIsOk := server.server.Register.Check(data) - if !checkIsOk { - _ = c.Close() - continue - } - tunnelId := 0 - _, tunnelList, err := service.NetworkTunnel().GetTunnelList(ctx, &interlModel.GetNetworkTunnelListInput{ - ServiceId: server.server.Id, - DeviceKey: deviceKey, - }) - if err != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(err.Error())) - continue - } - if len(tunnelList) == 0 { - heartbeatData, _ := json.Marshal(server.server.Heartbeat) - var addTunnelError error - if tunnelId, addTunnelError = service.NetworkTunnel().AddTunnel(ctx, interlModel.NetworkTunnelAddInput{ - ServerId: server.server.Id, - Name: deviceKey, - Types: "server-udp", - Addr: c.LocalAddr().String(), - Remote: c.RemoteAddr().String(), - Heartbeat: string(heartbeatData), - Protoccol: "", - Status: 0, - Remark: "", - }); addTunnelError != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(addTunnelError.Error())) - continue - } - } else { - tunnelId = tunnelList[0].Id - if editTunnelError := service.NetworkTunnel().EditTunnel(ctx, interlModel.NetworkTunnelEditInput{ - Id: tunnelList[0].Id, - NetworkTunnelAddInput: interlModel.NetworkTunnelAddInput{ - ServerId: tunnelList[0].ServerId, - Name: tunnelList[0].Name, - Types: tunnelList[0].Types, - Addr: c.LocalAddr().String(), - Remote: c.RemoteAddr().String(), - Retry: tunnelList[0].Retry, - Heartbeat: tunnelList[0].Heartbeat, - Serial: tunnelList[0].Serial, - Protoccol: tunnelList[0].Protoccol, - Status: tunnelList[0].Status, - Remark: tunnelList[0].Remark, - }, - }); editTunnelError != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionError, server.server.Id), []byte(editTunnelError.Error())) - continue - } - } - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOnline, server.server.Id), nil) - - tnl = newServerUdpTunnel(deviceKey, tunnelId, c, addr) - server.children[tunnelId] = tnl - ServerTunnelAction(ctx, server.server.Id, deviceKey) - TunnelOnlineAction(ctx, tunnelId, deviceKey) - } - - server.running = false - }() - - return nil -} - -// Close 关闭 -func (server *ServerUDP) Close() (err error) { - ServerCloseAction(server.server.Id) - //close tunnels - if server.children != nil { - for _, l := range server.children { - _ = l.Close() - } - } - return server.listener.Close() -} - -// GetTunnel 获取链接 -func (server *ServerUDP) GetTunnel(id int) tunnelinstance.TunnelInstance { - return server.children[id] -} - -func (server *ServerUDP) RemoveTunnel(id int) { - delete(server.children, id) -} - -func (server *ServerUDP) Running() bool { - return server.running -} diff --git a/network/core/server.go b/network/core/server.go deleted file mode 100644 index 2b5104a..0000000 --- a/network/core/server.go +++ /dev/null @@ -1,35 +0,0 @@ -package core - -import ( - "context" - "fmt" - "github.com/sagoo-cloud/sagooiot/network/core/tunnelinstance" - "github.com/sagoo-cloud/sagooiot/network/model" -) - -// Server 通道 -type ServerInstance interface { - Open(ctx context.Context) error - Close() error - GetTunnel(id int) tunnelinstance.TunnelInstance - RemoveTunnel(id int) - Running() bool -} - -// NewServer 创建通道 -func NewServer(server *model.Server) (ServerInstance, error) { - var svr ServerInstance - var err error - switch server.Type { - case "tcp": - svr = newServerTCP(server) - break - case "udp": - svr = newServerUDP(server) - break - default: - return nil, fmt.Errorf("Unsupport type %s ", server.Type) - } - - return svr, err -} diff --git a/network/core/server/base/base.go b/network/core/server/base/base.go new file mode 100644 index 0000000..efaa070 --- /dev/null +++ b/network/core/server/base/base.go @@ -0,0 +1,15 @@ +package base + +import ( + "context" + "sagooiot/network/core/tunnel/base" +) + +// Server 通道 +type ServerInstance interface { + Open(ctx context.Context) error + Close() error + GetTunnel(id string) base.TunnelInstance + RemoveTunnel(id string) + Running() bool +} diff --git a/network/core/server/base/server-tunnel.go b/network/core/server/base/server-tunnel.go new file mode 100644 index 0000000..9c8c63d --- /dev/null +++ b/network/core/server/base/server-tunnel.go @@ -0,0 +1,105 @@ +package base + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" +) + +const ( + SagooServerTunnelPrefix = "sagoo-server-tunnel" +) + +type ServerTunnel struct { + // server的id + ServerId int + // server通道id + TunnelId string + // 设备key + DeviceKey string + // server通道类型,暂时只支持tcp, udp + Type string + // server通道状态,0:离线,1:在线 + Status int + // server通道内地地址 + LocalAddr string + // server通道远程地址 + RemoteAddr string + // server通道备注 + Remark string `json:"remark" description:"备注"` +} + +// AddOrEditServerTunnel 添加或编辑server通道 +func AddOrEditServerTunnel(ctx context.Context, t ServerTunnel) (tunnelId string, err error) { + tunnelJson, _ := json.Marshal(t) + tunnelId = fmt.Sprintf("%s-%s", SagooServerTunnelPrefix, t.DeviceKey) + t.TunnelId = tunnelId + _, err = g.Redis().Do(ctx, "SET", fmt.Sprintf("%s-%s", SagooServerTunnelPrefix, t.DeviceKey), string(tunnelJson)) + return tunnelId, err +} + +// GetServerTunnel 获取server通道 +func GetServerTunnel(ctx context.Context, deviceKey string) (t ServerTunnel, err error) { + tunnelVar, err := g.Redis().Do(ctx, "GET", fmt.Sprintf("%s-%s", SagooServerTunnelPrefix, deviceKey)) + if err != nil { + return t, err + } + err = json.Unmarshal([]byte(tunnelVar.String()), &t) + return +} + +// GetServerTunnelList 获取server通道列表 +func GetServerTunnelList(ctx context.Context) (listT []ServerTunnel, err error) { + tunnelListVar, err := g.Redis().Do(ctx, "KEYS", fmt.Sprintf("%s-*", SagooServerTunnelPrefix)) + if err != nil { + return listT, err + } + for _, node := range tunnelListVar.Strings() { + var t ServerTunnel + if err = json.Unmarshal([]byte(node), &t); err != nil { + return listT, err + } + listT = append(listT, t) + } + return listT, nil +} + +func GetTunnelIdByDeviceKey(ctx context.Context, deviceKey string) (tunnelId string) { + return fmt.Sprintf("%s-%s", SagooServerTunnelPrefix, deviceKey) +} + +// DeleteServerTunnel 删除server通道 +func DeleteServerTunnel(ctx context.Context, deviceKey string) (t ServerTunnel, err error) { + t, err = GetServerTunnel(ctx, deviceKey) + if err != nil { + return t, err + } + _, err = g.Redis().Do(ctx, "DEL", fmt.Sprintf("%s-%s", SagooServerTunnelPrefix, deviceKey)) + return t, err +} + +// TunnelOnline server通道上线 +func (t ServerTunnel) TunnelOnline(ctx context.Context) error { + t, err := GetServerTunnel(ctx, t.DeviceKey) + if err != nil { + return err + } + if t.Status == 1 { + return nil + } else { + t.Status = 1 + _, err = AddOrEditServerTunnel(ctx, t) + return err + } +} + +// TunnelOffline server通道下线 +func (t ServerTunnel) TunnelOffline(ctx context.Context) error { + _, err := GetServerTunnel(ctx, t.DeviceKey) + if err != nil { + return err + } + _, err = DeleteServerTunnel(ctx, t.DeviceKey) + return err +} diff --git a/network/core/server/common/server-common.go b/network/core/server/common/server-common.go new file mode 100644 index 0000000..8d52c99 --- /dev/null +++ b/network/core/server/common/server-common.go @@ -0,0 +1,45 @@ +package common + +import ( + "context" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/network/core/device" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/network/model" + "strconv" + "time" + + "github.com/gogf/gf/v2/frame/g" +) + +func ServerOpenAction(serverId int) { + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServerTunnel, consts.ActionOpen, strconv.Itoa(serverId)), nil) +} + +func ServerCloseAction(serverId int) { + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServerTunnel, consts.ActionClose, strconv.Itoa(serverId)), nil) +} + +func ServerTunnelAction(ctx context.Context, serverId int, deviceKey string) { + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServerTunnel, consts.ActionTunnel, strconv.Itoa(serverId)), nil) + deviceDetail, _ := service.DevDevice().Get(ctx, deviceKey) + if deviceDetail == nil { + g.Log().Debugf(ctx, "deviceKey:%s not found,ignore", deviceKey) + return + } + _, err := device.LoadDevice(ctx, deviceDetail.Key) + if err != nil { + g.Log().Error(ctx, err) + } + if deviceDetail != nil && deviceDetail.Status != consts.DeviceStatueOnline { + if dataBusOfflineErr := baseLogic.Online(ctx, model.DeviceOnlineMessage{ + DeviceKey: deviceKey, + ProductKey: deviceDetail.Product.Key, + Timestamp: time.Now().Unix(), + }); dataBusOfflineErr != nil { + g.Log().Errorf(ctx, "online err:%v", dataBusOfflineErr) + } + } +} diff --git a/network/core/server/common/utils.go b/network/core/server/common/utils.go new file mode 100644 index 0000000..9a59578 --- /dev/null +++ b/network/core/server/common/utils.go @@ -0,0 +1,10 @@ +package common + +import "strings" + +func ResolvePort(addr string) string { + if strings.IndexByte(addr, ':') == -1 { + return ":" + addr + } + return addr +} diff --git a/network/core/server-manager.go b/network/core/server/server-manager.go similarity index 69% rename from network/core/server-manager.go rename to network/core/server/server-manager.go index c2a564f..d6b7736 100644 --- a/network/core/server-manager.go +++ b/network/core/server/server-manager.go @@ -1,13 +1,15 @@ -package core +package server import ( "context" "fmt" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - interModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" + "sagooiot/internal/consts" + interModel "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/network/core/mapper" + "sagooiot/network/core/tunnel" + "sagooiot/network/model" "sync" ) @@ -18,7 +20,7 @@ func startServer(ctx context.Context, server *model.Server) error { if err != nil { return err } - allServers.Store(server.Id, &Server{ + allServers.Store(server.Id, &tunnel.Server{ Server: *server, Instance: svr, }) @@ -47,23 +49,30 @@ func LoadServers(ctx context.Context) error { } var allServerModels = make([]model.Server, len(networkServerListRes)) for index, node := range networkServerListRes { - allServerModels[index] = MapperServer(ctx, *node) + if node != nil { + allServerModels[index] = mapper.Server(ctx, node) + } } for index := range allServerModels { - go func(server model.Server) error { + if err := func(server model.Server) error { if server.Disabled { + return nil } go func() { - err := startServer(ctx, &server) - if err != nil { - g.Log().Errorf(ctx, "服务启动失败!服务名称:%s,无法启动。错误信息:%s", server.Name, server.Addr, err) + if err := startServer(ctx, &server); err != nil { + g.Log().Errorf(ctx, "服务启动失败!服务名称:%s - %s,无法启动。错误信息:%v", server.Name, server.Addr, err) } + g.Log().Debugf(ctx, "网络服务:服务名称:%s,启动端口:%s ", server.Name, server.Addr) + }() return nil - }(allServerModels[index]) + }(allServerModels[index]); err != nil { + g.Log().Error(ctx, err.Error()) + } } + return nil } @@ -73,7 +82,7 @@ func LoadServer(ctx context.Context, id int) error { if networkServerResErr != nil { return networkServerResErr } - server := MapperServer(ctx, *networkServerRes) + server := mapper.Server(ctx, networkServerRes) if server.Disabled { return nil } @@ -85,10 +94,10 @@ func LoadServer(ctx context.Context, id int) error { } // GetServer 获取通道 -func GetServer(id int) *Server { +func GetServer(id int) *tunnel.Server { d, ok := allServers.Load(id) if ok { - return d.(*Server) + return d.(*tunnel.Server) } return nil } @@ -96,7 +105,7 @@ func GetServer(id int) *Server { func RemoveServer(id int) error { d, ok := allServers.LoadAndDelete(id) if ok { - tnl := d.(*Server) + tnl := d.(*tunnel.Server) return tnl.Instance.Close() } return nil //error diff --git a/network/core/server/server.go b/network/core/server/server.go new file mode 100644 index 0000000..7dcf63f --- /dev/null +++ b/network/core/server/server.go @@ -0,0 +1,29 @@ +package server + +import ( + "fmt" + "sagooiot/network/core/server/base" + "sagooiot/network/core/server/tcp" + "sagooiot/network/model" +) + +// NewServer 创建通道 +func NewServer(server *model.Server) (base.ServerInstance, error) { + var svr base.ServerInstance + var err error + switch server.Type { + case "tcp": + svr = tcp.NewServerTCP(server) + break + case "udp": + break + case "http": + break + case "websocket": + break + default: + return nil, fmt.Errorf("Unsupport type %s ", server.Type) + } + + return svr, err +} diff --git a/network/core/server/tcp/server-tcp-tunnel.go b/network/core/server/tcp/server-tcp-tunnel.go new file mode 100644 index 0000000..8ab7fca --- /dev/null +++ b/network/core/server/tcp/server-tcp-tunnel.go @@ -0,0 +1,82 @@ +package tcp + +import ( + "context" + "errors" + "github.com/gogf/gf/v2/frame/g" + "net" + "sagooiot/internal/consts" + "sagooiot/network/core/server/base" + "sagooiot/network/core/tunnel" + "sagooiot/network/core/tunnel/action" +) + +// ServerTcpTunnel 网络连接 +type ServerTcpTunnel struct { + serverId int + deviceKey string + *tunnel.TunnelBase +} + +func newServerTcpTunnel(ctx context.Context, serverId int, deviceKey string, conn net.Conn) (*ServerTcpTunnel, error) { + baseServerTunnelInfo := base.ServerTunnel{ + ServerId: serverId, + DeviceKey: deviceKey, + Type: "tcp", + Status: consts.TunnelIsOnLine, + LocalAddr: conn.LocalAddr().String(), + RemoteAddr: conn.RemoteAddr().String(), + Remark: "", + } + var err error + baseServerTunnelInfo.TunnelId, err = base.AddOrEditServerTunnel(ctx, baseServerTunnelInfo) + if err != nil { + return nil, err + } + g.Log().Debug(ctx, "newServerTcpTunnel", serverId, deviceKey, conn.LocalAddr().String(), conn.RemoteAddr().String()) + tunnelBase := tunnel.TunnelBase{ + TunnelId: baseServerTunnelInfo.TunnelId, + Link: conn, + ServerId: serverId, + } + tunnelBase.SetRunning(true) + tunnelBase.SetOnline(true) + return &ServerTcpTunnel{serverId: serverId, deviceKey: deviceKey, TunnelBase: &tunnelBase}, nil +} + +func (l *ServerTcpTunnel) Open(ctx context.Context) error { + return errors.New("ServerTcpTunnel cannot open") +} + +func (l *ServerTcpTunnel) receive(ctx context.Context) { + + if err := action.TunnelOnlineAction(ctx, l.serverId, l.TunnelId, l.deviceKey); err != nil { + g.Log().Errorf(ctx, "tunnel online error: %v", err) + return + } + buf := make([]byte, 1024) + for { + n, err := l.Link.Read(buf) + if err != nil { + l.OnClose() + break + } + if n == 0 { + continue + } + if l.GetPipe() != nil { + _, err = l.GetPipe().Write(buf[:n]) + if err != nil { + l.SetPipe(nil) + } else { + continue + } + } + go l.TunnelBase.ReadData(ctx, l.deviceKey, buf[:n]) + } + l.SetRunning(false) + l.SetOnline(false) + + action.TunnelOfflineAction(ctx, l.serverId, l.TunnelId, l.deviceKey) + +} diff --git a/network/core/server/tcp/server-tcp.go b/network/core/server/tcp/server-tcp.go new file mode 100644 index 0000000..0d31ea8 --- /dev/null +++ b/network/core/server/tcp/server-tcp.go @@ -0,0 +1,106 @@ +package tcp + +import ( + "context" + "errors" + "github.com/gogf/gf/v2/frame/g" + "net" + "sagooiot/network/core/server/common" + "sagooiot/network/core/tunnel/base" + "sagooiot/network/model" +) + +type ServerTCP struct { + server *model.Server + + children map[string]*ServerTcpTunnel + + listener *net.TCPListener + + running bool +} + +func NewServerTCP(server *model.Server) *ServerTCP { + svr := &ServerTCP{ + server: server, + children: make(map[string]*ServerTcpTunnel), + } + return svr +} + +func (server *ServerTCP) Open(ctx context.Context) error { + if server.running { + return errors.New("server is opened") + } + common.ServerOpenAction(server.server.Id) + + addr, err := net.ResolveTCPAddr("tcp4", common.ResolvePort(server.server.Addr)) + if err != nil { + return err + } + server.listener, err = net.ListenTCP("tcp4", addr) + if err != nil { + return err + } + + server.running = true + go func() { + for { + c, acceptErr := server.listener.AcceptTCP() + if acceptErr != nil { + g.Log().Errorf(ctx, "accept tcp error: %s", acceptErr.Error()) + break + } + + buf := make([]byte, 1024) + n, readErr := c.Read(buf) + if readErr != nil { + g.Log().Errorf(ctx, "read tcp error: %v,local_addr:%s remote_addr:%s", readErr, c.LocalAddr().String(), c.RemoteAddr().String()) + _ = c.Close() + continue + } + data := buf[:n] + deviceKey, checkIsOk := server.server.Register.Check(data) + if !checkIsOk { + g.Log().Errorf(ctx, "register check not right,check_data:%s ,local_addr:%s remote_addr:%s", string(data), c.LocalAddr().String(), c.RemoteAddr().String()) + _ = c.Close() + continue + } + tnl, tnlErr := newServerTcpTunnel(ctx, server.server.Id, deviceKey, c) + if tnlErr != nil { + g.Log().Errorf(ctx, "new tcp tunnel error: %v,local_addr:%s remote_addr:%s", tnlErr, c.LocalAddr().String(), c.RemoteAddr().String()) + continue + } + server.children[tnl.TunnelId] = tnl + go func() { + tnl.receive(ctx) + server.RemoveTunnel(tnl.TunnelId) + }() + common.ServerTunnelAction(ctx, server.server.Id, deviceKey) + } + server.running = false + }() + return nil +} + +func (server *ServerTCP) Close() (err error) { + common.ServerCloseAction(server.server.Id) + if server.children != nil { + for _, l := range server.children { + _ = l.Close() + } + } + return server.listener.Close() +} + +func (server *ServerTCP) GetTunnel(id string) base.TunnelInstance { + return server.children[id] +} + +func (server *ServerTCP) RemoveTunnel(id string) { + delete(server.children, id) +} + +func (server *ServerTCP) Running() bool { + return server.running +} diff --git a/network/core/tunnel-base.go b/network/core/tunnel-base.go deleted file mode 100644 index 5cebcb5..0000000 --- a/network/core/tunnel-base.go +++ /dev/null @@ -1,175 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "errors" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/extend" - "github.com/sagoo-cloud/sagooiot/internal/service" - networkModel "github.com/sagoo-cloud/sagooiot/network/model" - "io" - "sync" - "time" -) - -type tunnelBase struct { - tunnelId int - - lock sync.Mutex - - link io.ReadWriteCloser - - running bool - online bool - first bool - - retry int - retryTimer *time.Timer - - pipe io.ReadWriteCloser - data chan []byte -} - -func (l *tunnelBase) Running() bool { - return l.running -} - -func (l *tunnelBase) Online() bool { - return l.online -} - -// Close 关闭 -func (l *tunnelBase) Close() error { - if l.retryTimer != nil { - l.retryTimer.Stop() - } - if !l.running { - return errors.New("tunnel closed") - } - TunnelCloseAction(l.tunnelId) - l.onClose() - return l.link.Close() -} - -func (l *tunnelBase) onClose() { - l.running = false - if l.pipe != nil { - _ = l.pipe.Close() - } - TunnelCloseAction(l.tunnelId) -} - -// Write 写 -func (l *tunnelBase) Write(data []byte) error { - if !l.running { - return errors.New("tunnel closed") - } - if l.pipe != nil { - return nil //透传模式下,直接抛弃 - } - _, err := l.link.Write(data) - return err -} - -func (l *tunnelBase) wait(duration time.Duration) ([]byte, error) { - select { - case <-time.After(duration): - return nil, errors.New("超时") - case buf := <-l.data: - return buf, nil - } -} - -func (l *tunnelBase) ReadData(ctx context.Context, deviceKey string, data []byte) { - res := string(data) - deviceDetail, err := service.DevDevice().Get(ctx, deviceKey) - if err != nil { - g.Log().Errorf(ctx, "get deviceInfo error: %w, deviceKey:%s, message ignored", err, deviceDetail.Key) - return - } - productDetail, productDetailErr := service.DevProduct().Get(ctx, deviceDetail.Product.Key) - if productDetailErr != nil || productDetail == nil { - g.Log().Errorf(ctx, "find product info error: %w, productKey:%s, message ignored", productDetailErr, deviceDetail.Product.Key) - return - } - if productDetail.MessageProtocol != "" { - if extend.GetProtocolPlugin() == nil { - return - } - // 通过消息协议插件解析数据 - pluginData, err := extend.GetProtocolPlugin().GetProtocolUnpackData(productDetail.MessageProtocol, data) - g.Log().Debug(context.TODO(), "GetProtocolUnpackData", pluginData) - if err != nil { - g.Log().Errorf(ctx, "get plugin error: %w, message:%s, message ignored", err, res) - return - } - if pluginData.Code != 0 { - g.Log().Errorf(ctx, "plugin parse error: code:%d message:%s, deviceKey:%s, data:%s, message ignored", pluginData.Code, pluginData.Message, deviceDetail.Key, string(data)) - return - } - pluginDataByte, _ := json.Marshal(pluginData.Data) - res = string(pluginDataByte) - } - var reportData networkModel.DefaultMessageType - if reportDataErr := json.Unmarshal([]byte(res), &reportData); reportDataErr != nil { - g.Log().Errorf(ctx, "parse data error: %w, topic:%s, message:%s, message ignored", reportDataErr, res) - return - } - messageRouter{ - ctx: ctx, - data: reportData.Data, - msgType: reportData.DataType, - deviceDetail: deviceDetail, - }.router() - -} - -func (l *tunnelBase) Ask(cmd []byte, timeout time.Duration) ([]byte, error) { - if !l.running { - return nil, errors.New("tunnel closed") - } - - //堵塞 - l.lock.Lock() - defer l.lock.Unlock() //自动解锁 - - _, err := l.link.Write(cmd) - if err != nil { - return nil, err - } - return l.wait(timeout) -} - -func (l *tunnelBase) Pipe(pipe io.ReadWriteCloser) { - //关闭之前的透传 - if l.pipe != nil { - _ = l.pipe.Close() - } - - l.pipe = pipe - //传入空,则关闭 - if pipe == nil { - return - } - - buf := make([]byte, 1024) - for { - n, err := pipe.Read(buf) - if err != nil { - //if err == io.EOF { - // continue - //} - //pipe关闭,则不再透传 - break - } - //将收到的数据转发出去 - n, err = l.link.Write(buf[:n]) - if err != nil { - //发送失败,说明连接失效 - _ = pipe.Close() - break - } - } - l.pipe = nil -} diff --git a/network/core/tunnel-common.go b/network/core/tunnel-common.go deleted file mode 100644 index 9b27de8..0000000 --- a/network/core/tunnel-common.go +++ /dev/null @@ -1,89 +0,0 @@ -package core - -import ( - "context" - "encoding/json" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" - "time" -) - -func TunnelOpenAction(tunnelId int) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOpen, tunnelId), nil) -} - -func TunnelCloseAction(tunnelId int) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionClose, tunnelId), nil) -} - -func TunnelOnlineAction(ctx context.Context, tunnelId int, deviceKey string) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOnline, tunnelId), nil) - deviceRes, err := service.DevDevice().Get(ctx, deviceKey) - if err != nil { - g.Log().Error(ctx, err) - return - } - deviceDetail := GetDevice(uint64(deviceRes.Id)) - if deviceDetail != nil { - err = deviceDetail.Start(ctx) - if err != nil { - g.Log().Error(ctx, err) - return - } else { - if deviceRes != nil && deviceRes.Status != consts.DeviceStatueOnline { - onlineMessage, _ := json.Marshal(model.DeviceOnlineMessage{ - DeviceKey: deviceRes.DevDevice.Key, - Timestamp: time.Now().Unix(), - }) - if dataBusOfflineErr := mqtt.Publish(consts.GetDataBusWrapperTopic(deviceRes.Product.Key, deviceRes.DevDevice.Key, consts.DataBusOffline), onlineMessage); dataBusOfflineErr != nil { - g.Log().Errorf(ctx, "publish data error: %w, topic:%s, message:%s, message ignored", dataBusOfflineErr, - consts.GetDataBusWrapperTopic(deviceRes.Product.Key, deviceRes.DevDevice.Key, consts.DataBusOffline), - string(onlineMessage)) - return - } - } - } - } -} - -func TunnelOfflineAction(ctx context.Context, serverId, tunnelId int) { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOffline, tunnelId), nil) - tunnelInfo, err := service.NetworkTunnel().GetTunnelById(ctx, tunnelId) - - //TODO log error - if err == nil && tunnelInfo != nil { - deviceDetail, _ := service.DevDevice().Get(ctx, tunnelInfo.DeviceKey) - if deviceDetail == nil { - g.Log().Errorf(ctx, "deviceKey:%s not found,ignore", tunnelInfo.DeviceKey) - return - } - if deviceStopError := GetDevice(uint64(deviceDetail.Id)).Stop(); deviceStopError != nil { - g.Log().Errorf(ctx, "Stop device error:%w ,ignore", deviceStopError) - } - if deviceDetail != nil && deviceDetail.Status == consts.DeviceStatueOnline { - onlineMessage, _ := json.Marshal(model.DeviceOnlineMessage{ - DeviceKey: tunnelInfo.DeviceKey, - Timestamp: time.Now().Unix(), - }) - if dataBusOfflineErr := mqtt.Publish(consts.GetDataBusWrapperTopic(deviceDetail.Product.Key, tunnelInfo.DeviceKey, consts.DataBusOffline), onlineMessage); dataBusOfflineErr != nil { - g.Log().Errorf(ctx, "publish data error: %w, topic:%s, message:%s, message ignored", dataBusOfflineErr, - consts.GetDataBusWrapperTopic(deviceDetail.Product.Key, tunnelInfo.DeviceKey, consts.DataBusOffline), - string(onlineMessage)) - return - } - } - - } else if err != nil { - g.Log().Errorf(ctx, "getTunnel ifno error:%w ,ignore", err) - } - - if serverId != 0 { - server := GetServer(serverId) - if server != nil { - server.Instance.RemoveTunnel(tunnelId) - } - } -} diff --git a/network/core/tunnel/action/tunnel-common.go b/network/core/tunnel/action/tunnel-common.go new file mode 100644 index 0000000..e90b174 --- /dev/null +++ b/network/core/tunnel/action/tunnel-common.go @@ -0,0 +1,86 @@ +package action + +import ( + "context" + "errors" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/network/core/device" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/network/model" + "time" +) + +func TunnelOpenAction(serverId int, tunnelId string) { + topic := consts.DataBusTunnel + if serverId != 0 { + topic = consts.DataBusServerTunnel + } + _ = mqtt.Publish(consts.GetWrapperTopic(topic, consts.ActionOpen, tunnelId), nil) +} + +func TunnelCloseAction(serverId int, tunnelId string) { + topic := consts.DataBusTunnel + if serverId != 0 { + topic = consts.DataBusServerTunnel + } + _ = mqtt.Publish(consts.GetWrapperTopic(topic, consts.ActionClose, tunnelId), nil) +} + +func TunnelOnlineAction(ctx context.Context, serverId int, tunnelId string, deviceKey string) (err error) { + topic := consts.DataBusTunnel + if serverId != 0 { + topic = consts.DataBusServerTunnel + } + _ = mqtt.Publish(consts.GetWrapperTopic(topic, consts.ActionOnline, tunnelId), nil) + deviceRes, err := service.DevDevice().Get(ctx, deviceKey) + if err != nil { + err = errors.New("device not found") + return + } + deviceDetail := device.GetDevice(uint64(deviceRes.Id)) + if deviceDetail != nil { + err = deviceDetail.Start(ctx) + if err != nil { + g.Log().Error(ctx, err) + return + } else { + if deviceRes != nil && deviceRes.Status != consts.DeviceStatueOnline { + if dataBusOfflineErr := baseLogic.Online(ctx, model.DeviceOnlineMessage{ + DeviceKey: deviceRes.DevDevice.Key, + ProductKey: deviceRes.Product.Key, + Timestamp: time.Now().Unix(), + }); dataBusOfflineErr != nil { + g.Log().Errorf(ctx, "online err:%v", dataBusOfflineErr) + } + } + } + } + return +} + +func TunnelOfflineAction(ctx context.Context, serverId int, tunnelId, deviceKey string) { + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOffline, tunnelId), nil) + if deviceKey != "" { + deviceDetail, _ := service.DevDevice().Get(ctx, deviceKey) + if deviceDetail == nil { + g.Log().Errorf(ctx, "deviceKey:%s not found,ignore", deviceKey) + return + } + if deviceStopError := device.GetDevice(uint64(deviceDetail.Id)).Stop(); deviceStopError != nil { + g.Log().Errorf(ctx, "Stop device error:%v ,ignore", deviceStopError) + } + if deviceDetail != nil && deviceDetail.Status == consts.DeviceStatueOnline { + if offlineErr := baseLogic.Offline(ctx, model.DeviceOfflineMessage{ + DeviceKey: deviceKey, + ProductKey: deviceDetail.Product.Key, + Timestamp: time.Now().Unix(), + }); offlineErr != nil { + g.Log().Errorf(ctx, "offline err:%v", offlineErr) + } + } + + } +} diff --git a/network/core/tunnel/base/base.go b/network/core/tunnel/base/base.go new file mode 100644 index 0000000..857472c --- /dev/null +++ b/network/core/tunnel/base/base.go @@ -0,0 +1,72 @@ +package base + +import ( + "context" + "io" + "sagooiot/pkg/iotModel/topicModel" + "sync" + "time" +) + +// Tunnel 通道 +type TunnelInstance interface { + Write(data []byte) error + + Open(context.Context) error + + Close() error + + Running() bool + + Online() bool + + //Pipe 透传 + Pipe(pipe io.ReadWriteCloser) + + //Ask 发送指令,接收数据 + Ask(cmd []byte, timeout time.Duration) ([]byte, error) +} + +type ModelType struct { + LogType string + GetTopicWithInfo func(deviceKey, productKey, identify string) string + Handle func(context.Context, topicModel.TopicHandlerData) error +} + +type ModelTypeMap struct { + sync.RWMutex + MapInfo map[string]ModelType +} + +var ModelMapInfo = ModelTypeMap{MapInfo: map[string]ModelType{}} + +const ( + UpProperty = "upProperty" + UpSetProperty = "upSetProperty" + UpEvent = "upEvent" + UpBatch = "upBatch" + UpServiceOutput = "upServiceOutput" + + UpSetConfig = "upSetConfig" + + UpConfigGet = "upConfigGet" + + UpInform = "upInform" + + UpProcess = "upProcess" + + DownProperty = "downProperty" + DownServiceInput = "downServiceInput" +) + +func RegisterModelType(name string, modelType ModelType) { + ModelMapInfo.Lock() + defer ModelMapInfo.Unlock() + ModelMapInfo.MapInfo[name] = modelType +} + +func GetModelHandle(name string) ModelType { + ModelMapInfo.Lock() + defer ModelMapInfo.Unlock() + return ModelMapInfo.MapInfo[name] +} diff --git a/network/core/tunnel/tunnel-base.go b/network/core/tunnel/tunnel-base.go new file mode 100644 index 0000000..7fbe86c --- /dev/null +++ b/network/core/tunnel/tunnel-base.go @@ -0,0 +1,246 @@ +package tunnel + +import ( + "context" + "encoding/json" + "errors" + "github.com/gogf/gf/v2/frame/g" + "io" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/network/core/logic/baseLogic" + "sagooiot/network/core/tunnel/action" + tunelBase "sagooiot/network/core/tunnel/base" + networkModel "sagooiot/network/model" + "sagooiot/pkg/iotModel/topicModel" + "sagooiot/pkg/jsinterpreter" + "sagooiot/pkg/plugins" + "sync" + "time" +) + +type TunnelBase struct { + TunnelId string + ServerId int + Lock sync.Mutex + + Link io.ReadWriteCloser + + running bool + online bool + First bool + + retry int + retryTimer *time.Timer + + pipe io.ReadWriteCloser + data chan []byte +} + +func (l *TunnelBase) Running() bool { + return l.running +} + +func (l *TunnelBase) SetRunning(isRunning bool) { + l.running = isRunning +} + +func (l *TunnelBase) Online() bool { + return l.online +} + +func (l *TunnelBase) SetOnline(isOnline bool) { + l.online = isOnline +} + +func (l *TunnelBase) GetPipe() io.ReadWriteCloser { + return l.pipe +} + +func (l *TunnelBase) SetPipe(pipe io.ReadWriteCloser) { + l.pipe = pipe +} + +// Close 关闭 +func (l *TunnelBase) Close() error { + if l.retryTimer != nil { + l.retryTimer.Stop() + } + if !l.running { + return errors.New("tunnel closed") + } + action.TunnelCloseAction(l.ServerId, l.TunnelId) + l.OnClose() + return l.Link.Close() +} + +func (l *TunnelBase) OnClose() { + l.running = false + if l.pipe != nil { + _ = l.pipe.Close() + } + action.TunnelCloseAction(l.ServerId, l.TunnelId) +} + +// Write 写 +func (l *TunnelBase) Write(data []byte) error { + if !l.running { + return errors.New("tunnel closed") + } + if l.pipe != nil { + return nil //透传模式下,直接抛弃 + } + //TODO 需要加入黏包的分割字符,不然客户端会阻塞 + //data = append(data, []byte("\n")...) + _, err := l.Link.Write(data) + return err +} + +func (l *TunnelBase) Wait(duration time.Duration) ([]byte, error) { + select { + case <-time.After(duration): + return nil, errors.New("超时") + case buf := <-l.data: + return buf, nil + } +} + +func (l *TunnelBase) ReadData(ctx context.Context, deviceKey string, data []byte) { + deviceDetail, err := service.DevDevice().Get(ctx, deviceKey) + if err != nil { + g.Log().Errorf(ctx, "get deviceInfo error: %v, deviceKey:%s, message ignored", err, deviceKey) + return + } + productDetail, productDetailErr := service.DevProduct().Detail(ctx, deviceDetail.Product.Key) + if productDetailErr != nil || productDetail == nil { + g.Log().Errorf(ctx, "find product info error: %v, productKey:%s, message ignored", productDetailErr, deviceDetail.Product.Key) + return + } + if deviceDetail != nil && deviceDetail.Status != consts.DeviceStatueOnline { + if deviceOnlineErr := baseLogic.Online(ctx, networkModel.DeviceOnlineMessage{ + DeviceKey: deviceKey, + ProductKey: deviceDetail.Product.Key, + Timestamp: time.Now().Unix(), + }); deviceOnlineErr != nil { + g.Log().Errorf(ctx, "device online error: %v, deviceKey:%s, message:%s, message ignored", deviceOnlineErr, deviceKey, string(data)) + } + } + l.router(ctx, productDetail, deviceDetail, data) +} + +func (l *TunnelBase) Ask(cmd []byte, timeout time.Duration) ([]byte, error) { + if !l.running { + return nil, errors.New("tunnel closed") + } + + //堵塞 + l.Lock.Lock() + defer l.Lock.Unlock() //自动解锁 + + _, err := l.Link.Write(cmd) + if err != nil { + return nil, err + } + return l.Wait(timeout) +} + +func (l *TunnelBase) Pipe(pipe io.ReadWriteCloser) { + //关闭之前的透传 + if l.pipe != nil { + _ = l.pipe.Close() + } + + l.pipe = pipe + //传入空,则关闭 + if pipe == nil { + return + } + + buf := make([]byte, 1024) + for { + n, err := pipe.Read(buf) + if err != nil { + //if err == io.EOF { + // continue + //} + //pipe关闭,则不再透传 + break + } + //将收到的数据转发出去 + n, err = l.Link.Write(buf[:n]) + if err != nil { + //发送失败,说明连接失效 + _ = pipe.Close() + break + } + } + l.pipe = nil +} + +func (l *TunnelBase) router(ctx context.Context, productDetail *model.DetailProductOutput, deviceDetail *model.DeviceOutput, data []byte) { + res := string(data) + if productDetail.MessageProtocol != consts.DefaultProtocol && productDetail.MessageProtocol != "" { + if plugins.GetProtocolPlugin() == nil { + return + } + var err error + // 通过消息协议插件解析数据 + pluginData, err := plugins.GetProtocolPlugin().GetProtocolDecodeData(productDetail.MessageProtocol, data) + g.Log().Debug(context.TODO(), "GetProtocolDecodeData", pluginData) + if err != nil { + g.Log().Debugf(ctx, "get plugin error: %v, deviceKey:%s, data:%s, message ignored", err, deviceDetail.Key, string(data)) + return + } + if pluginData.Code != 0 || pluginData.Data == nil { + g.Log().Debugf(ctx, "plugin parse error: code:%d message:%s, deviceKey:%s, data:%s, message ignored", pluginData.Code, pluginData.Message, deviceDetail.Key, string(data)) + return + } + pluginDataByte, _ := json.Marshal(pluginData.Data) + res = string(pluginDataByte) + } + // 如果有js脚本,根据js脚本处理解析后的数据,处理后的数据数据格式为默认的消息协议格式 + if productDetail.ScriptInfo != "" { + var runScriptErr error + res, runScriptErr = jsinterpreter.RunScript(res, productDetail.ScriptInfo) + if runScriptErr != nil { + g.Log().Errorf(ctx, "runScriptErr error: %v, deviceKey:%s, data:%s, message ignored", runScriptErr, deviceDetail.Key, string(data)) + return + } + } + + var dataInfo = map[string]interface{}{} + if err := json.Unmarshal([]byte(res), &dataInfo); err != nil { + g.Log().Errorf(ctx, "json.Unmarshal error: %v, deviceKey:%s, data:%s, message ignored", err, deviceDetail.Key, string(data)) + return + } + modelFuncName, ok := dataInfo["model_func_name"].(string) + if !ok { + g.Log().Errorf(ctx, "model_func_name not found, deviceKey:%s, data:%s, message ignored", deviceDetail.Key, string(data)) + return + } + modelIdentifyName, ok := dataInfo["model_func_identify"].(string) + if !ok { + g.Log().Errorf(ctx, "model_func_identify not found, deviceKey:%s, data:%s, message ignored", deviceDetail.Key, string(data)) + return + } + handleF := tunelBase.GetModelHandle(modelFuncName) + if modelFuncName == tunelBase.UpProperty { + modelIdentifyName = "property" + } + if err := handleF.Handle(ctx, topicModel.TopicHandlerData{ + Topic: handleF.GetTopicWithInfo(productDetail.Key, deviceDetail.Key, modelIdentifyName), + ProductKey: productDetail.Key, + DeviceKey: deviceDetail.Key, + PayLoad: []byte(res), + //ProductDetail: productDetail, + DeviceDetail: deviceDetail, + }); err != nil && err.Error() != "ignore" { + g.Log().Infof(ctx, "handleF error: %v, topic:%s, message:%s, message ignored", err, handleF.GetTopicWithInfo(productDetail.Key, deviceDetail.Key, modelIdentifyName), string(data)) + return + } + // 记录原始日志,网关批量的放在网关内部处理 + if handleF.LogType != consts.MsgTypeGatewayBatch { + baseLogic.InertTdLog(ctx, handleF.LogType, deviceDetail.Key, string(data)) + } +} diff --git a/network/core/tunnel-client.go b/network/core/tunnel/tunnel-client.go similarity index 69% rename from network/core/tunnel-client.go rename to network/core/tunnel/tunnel-client.go index 0f8db76..024e42a 100644 --- a/network/core/tunnel-client.go +++ b/network/core/tunnel/tunnel-client.go @@ -1,28 +1,30 @@ -package core +package tunnel import ( "context" "errors" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/consts" - interlModel "github.com/sagoo-cloud/sagooiot/internal/model" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/model" "net" + "sagooiot/internal/consts" + interlModel "sagooiot/internal/model" + "sagooiot/internal/mqtt" + "sagooiot/internal/service" + "sagooiot/network/core/tunnel/action" + "sagooiot/network/model" + "strconv" "time" ) // TunnelClient 网络链接 type TunnelClient struct { - tunnelBase + TunnelBase tunnelInfo *model.Tunnel net string } func newTunnelClient(tunnel *model.Tunnel, net string) *TunnelClient { return &TunnelClient{ - tunnelBase: tunnelBase{tunnelId: int(tunnel.Id)}, + TunnelBase: TunnelBase{TunnelId: strconv.Itoa(int(tunnel.Id))}, tunnelInfo: tunnel, net: net, } @@ -33,7 +35,7 @@ func (client *TunnelClient) Open(ctx context.Context) error { if client.running { return errors.New("client is opened") } - TunnelOpenAction(client.tunnelId) + action.TunnelOpenAction(0, client.TunnelId) //发起连接 conn, err := net.Dial(client.net, client.tunnelInfo.Addr) @@ -42,7 +44,7 @@ func (client *TunnelClient) Open(ctx context.Context) error { return err } client.retry = 0 - client.link = conn + client.Link = conn //开始接收数据 go client.receive(ctx) @@ -55,10 +57,10 @@ func (client *TunnelClient) Open(ctx context.Context) error { Remote: conn.RemoteAddr().String(), }, }); editTunnelError != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionError, int(client.tunnelInfo.Id)), []byte(editTunnelError.Error())) + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionError, strconv.Itoa(int(client.tunnelInfo.Id))), []byte(editTunnelError.Error())) return editTunnelError } - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOnline, int(client.tunnelInfo.Id)), nil) + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionOnline, strconv.Itoa(int(client.tunnelInfo.Id))), nil) return nil } @@ -83,14 +85,18 @@ func (client *TunnelClient) receive(ctx context.Context) { tunnelInfo, tunnelInfoErr := service.NetworkTunnel().GetTunnelById(ctx, int(client.tunnelInfo.Id)) if tunnelInfoErr != nil { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionError, int(client.tunnelInfo.Id)), []byte(tunnelInfoErr.Error())) + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusTunnel, consts.ActionError, strconv.Itoa(int(client.tunnelInfo.Id))), []byte(tunnelInfoErr.Error())) } else { - TunnelOnlineAction(ctx, int(client.tunnelInfo.Id), tunnelInfo.DeviceKey) + onlineErr := action.TunnelOnlineAction(ctx, 0, strconv.Itoa(int(client.tunnelInfo.Id)), tunnelInfo.DeviceKey) + if onlineErr != nil { + g.Log().Errorf(ctx, "tunnel online error: %v", onlineErr) + return + } buf := make([]byte, 1024) for { - n, err := client.link.Read(buf) + n, err := client.Link.Read(buf) if err != nil { - client.onClose() + client.OnClose() break } if n == 0 { @@ -112,23 +118,23 @@ func (client *TunnelClient) receive(ctx context.Context) { continue } } - go client.tunnelBase.ReadData(ctx, tunnelInfo.DeviceKey, data) + go client.TunnelBase.ReadData(ctx, tunnelInfo.DeviceKey, data) } } client.running = false client.online = false - TunnelOfflineAction(ctx, 0, client.tunnelId) + action.TunnelOfflineAction(ctx, 0, client.TunnelId, tunnelInfo.DeviceKey) client.Retry(ctx) } // Close 关闭 func (client *TunnelClient) Close() error { client.running = false - TunnelCloseAction(client.tunnelId) + action.TunnelCloseAction(0, client.TunnelId) - if client.link != nil { - link := client.link - client.link = nil + if client.Link != nil { + link := client.Link + client.Link = nil return link.Close() } return errors.New("tunnel is closed") diff --git a/network/core/tunnel-manager.go b/network/core/tunnel/tunnel-manager.go similarity index 74% rename from network/core/tunnel-manager.go rename to network/core/tunnel/tunnel-manager.go index dfeb3dd..eb1dcc7 100644 --- a/network/core/tunnel-manager.go +++ b/network/core/tunnel/tunnel-manager.go @@ -1,11 +1,14 @@ -package core +package tunnel import ( "context" + "fmt" "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/internal/service" - "github.com/sagoo-cloud/sagooiot/network/core/tunnelinstance" - "github.com/sagoo-cloud/sagooiot/network/model" + "sagooiot/internal/service" + "sagooiot/network/core/mapper" + "sagooiot/network/core/server/base" + base2 "sagooiot/network/core/tunnel/base" + "sagooiot/network/model" "sync" ) @@ -13,18 +16,21 @@ var allTunnels sync.Map type Server struct { model.Server - Instance ServerInstance + Instance base.ServerInstance } type Tunnel struct { - Instance tunnelinstance.TunnelInstance + Instance base2.TunnelInstance } func startTunnel(ctx context.Context, tunnel *model.Tunnel) error { tnl, err := NewTunnel(tunnel) if err != nil { - // log.Error(err) - return err + g.Log().Error(ctx, err.Error()) + return nil + } + if tnl == nil { + return fmt.Errorf("new tunnel: %+#v tunnel failed", tunnel) } return tnl.Open(ctx) } @@ -37,7 +43,7 @@ func LoadTunnels(ctx context.Context) error { return err } for _, node := range tunnelRunList { - t := mapperTunnel(ctx, *node) + t := mapper.Tunnel(ctx, *node) allTunnelModels = append(allTunnelModels, &t) } @@ -58,7 +64,7 @@ func LoadTunnel(ctx context.Context, id int) error { if err != nil { return err } - tunnel := mapperTunnel(ctx, *tunnelInfo) + tunnel := mapper.Tunnel(ctx, *tunnelInfo) if tunnel.Disabled { return nil // TODO error ?? } diff --git a/network/core/tunnel.go b/network/core/tunnel/tunnel.go similarity index 50% rename from network/core/tunnel.go rename to network/core/tunnel/tunnel.go index 0d2c454..19ef9af 100644 --- a/network/core/tunnel.go +++ b/network/core/tunnel/tunnel.go @@ -1,15 +1,14 @@ -package core +package tunnel import ( "fmt" - "github.com/sagoo-cloud/sagooiot/network/core/tunnelinstance" - "github.com/sagoo-cloud/sagooiot/network/model" - "strings" + "sagooiot/network/core/tunnel/base" + "sagooiot/network/model" ) // NewTunnel 创建通道 -func NewTunnel(tunnel *model.Tunnel) (tunnelinstance.TunnelInstance, error) { - var tnl tunnelinstance.TunnelInstance +func NewTunnel(tunnel *model.Tunnel) (base.TunnelInstance, error) { + var tnl base.TunnelInstance switch tunnel.Type { case "serial": //TODO 等待补全 @@ -26,10 +25,3 @@ func NewTunnel(tunnel *model.Tunnel) (tunnelinstance.TunnelInstance, error) { } return tnl, nil } - -func resolvePort(addr string) string { - if strings.IndexByte(addr, ':') == -1 { - return ":" + addr - } - return addr -} diff --git a/network/core/tunnelinstance/tunnel-instance.go b/network/core/tunnelinstance/tunnel-instance.go deleted file mode 100644 index f3db43b..0000000 --- a/network/core/tunnelinstance/tunnel-instance.go +++ /dev/null @@ -1,26 +0,0 @@ -package tunnelinstance - -import ( - "context" - "io" - "time" -) - -// Tunnel 通道 -type TunnelInstance interface { - Write(data []byte) error - - Open(context.Context) error - - Close() error - - Running() bool - - Online() bool - - //Pipe 透传 - Pipe(pipe io.ReadWriteCloser) - - //Ask 发送指令,接收数据 - Ask(cmd []byte, timeout time.Duration) ([]byte, error) -} diff --git a/network/events/events.go b/network/events/events.go index 39fff15..3c84ab3 100644 --- a/network/events/events.go +++ b/network/events/events.go @@ -7,7 +7,7 @@ import ( type Handler func(args ...interface{}) -//EventInterface Events接口 +// EventInterface Events接口 type EventInterface interface { Emit(event string, data ...interface{}) On(event string, fn interface{}) @@ -24,7 +24,7 @@ type subscriber struct { once bool } -//Emit 发送消息 +// Emit 发送消息 func (e *EventEmitter) Emit(event string, data ...interface{}) { sub, ok1 := e.events.Load(event) subAll, ok2 := e.events.Load("*") @@ -45,7 +45,9 @@ func (e *EventEmitter) Emit(event string, data ...interface{}) { //args := make([]reflect.Value, 1+len(data)) subscribers.Range(func(key, value interface{}) bool { handler := value.(*subscriber) - handler.callback.Call(args[1:]) + if args != nil && len(args) > 2 { + handler.callback.Call(args[1:]) + } //处理仅订阅一次 if handler.once { subscribers.Delete(key) @@ -69,7 +71,7 @@ func (e *EventEmitter) Emit(event string, data ...interface{}) { } } -//On 监听 +// On 监听 func (e *EventEmitter) On(event string, fn interface{}) { callback := reflect.ValueOf(fn) val, ok := e.events.Load(event) @@ -84,7 +86,7 @@ func (e *EventEmitter) On(event string, fn interface{}) { }) } -//Once 监听一次 +// Once 监听一次 func (e *EventEmitter) Once(event string, fn interface{}) { callback := reflect.ValueOf(fn) val, ok := e.events.Load(event) @@ -99,7 +101,7 @@ func (e *EventEmitter) Once(event string, fn interface{}) { }) } -//Off 取消监听 +// Off 取消监听 func (e *EventEmitter) Off(event string, fn interface{}) { callback := reflect.ValueOf(fn) val, ok := e.events.Load(event) diff --git a/network/model/device.go b/network/model/device.go index 1b28353..038f182 100644 --- a/network/model/device.go +++ b/network/model/device.go @@ -22,12 +22,11 @@ type Product struct { // Device 设备 type Device struct { - Id uint64 `json:"id"` - TunnelId uint64 `json:"tunnel_id" boltholdIndex:"TunnelId"` - ProductId string `json:"product_id"` - - Name string `json:"name"` - Station int `json:"station"` + DeviceKey string `json:"device_key"` + TunnelId uint64 `json:"tunnel_id" boltholdIndex:"TunnelId"` + ProductKey string `json:"product_key"` + Name string `json:"name"` + Station int `json:"station"` Disabled bool `json:"disabled"` Created time.Time `json:"created" xorm:"created"` diff --git a/network/model/message.go b/network/model/message.go index e677d00..ad71b2a 100644 --- a/network/model/message.go +++ b/network/model/message.go @@ -1,13 +1,5 @@ package model -// 设备上报的报文解析为平台统一的消息,消息体结构如下 -type DefaultMessageType struct { - ReturnTime string `json:"return_time"` - DataType string `json:"data_type"` - DeviceKey string `json:"device_key"` - Data map[string]any `json:"data"` -} - // 通用的结构体 type ( Header struct { @@ -24,10 +16,11 @@ type ( MergeLatest bool `json:"mergeLatest" desc:"是否合并最新属性数据。设置此消息头后,将会把最新的消息合并到消息体里(需要开启最新数据存储。"` } Common struct { - H Header - DeviceKey string - MessageId string - Timestamp int64 + H Header + DeviceKey string + GatewayDeviceKey string + MessageId string + Timestamp int64 } ) @@ -96,13 +89,15 @@ type ( type ( Message any DeviceOnlineMessage struct { - DeviceKey string - Timestamp int64 + DeviceKey string + ProductKey string + Timestamp int64 } DeviceOfflineMessage struct { - DeviceKey string - Timestamp int64 + DeviceKey string + ProductKey string + Timestamp int64 } ) diff --git a/network/model/server.go b/network/model/server.go index 76ad0fa..bd0f59c 100644 --- a/network/model/server.go +++ b/network/model/server.go @@ -1,28 +1,36 @@ package model import ( - "github.com/sagoo-cloud/sagooiot/internal/consts" - "github.com/sagoo-cloud/sagooiot/internal/mqtt" "regexp" + "sagooiot/internal/consts" + "sagooiot/internal/mqtt" + "strconv" "time" ) type Server struct { - Id int `json:"id"` - Name string `json:"name"` - Type string `json:"type"` //tcp udp - Addr string `json:"addr"` - Register RegisterPacket `json:"register"` - Heartbeat HeartBeatPacket `json:"heartbeat"` - Options map[string]string `json:"options"` - Protocol Protocol `json:"protocol"` - Devices []DefaultDevice `json:"devices"` //默认设备 - Disabled bool `json:"disabled"` - Created time.Time `json:"created" xorm:"created"` + Id int `json:"id"` + Name string `json:"name"` + Type string `json:"type"` //tcp udp + Addr string `json:"addr"` + Register RegisterPacket `json:"register"` + Heartbeat HeartBeatPacket `json:"heartbeat"` + Options map[string]string `json:"options"` + Protocol Protocol `json:"protocol"` + Devices []DefaultDevice `json:"devices"` //默认设备 + Disabled bool `json:"disabled"` + Created time.Time `json:"created" xorm:"created"` + IsTls uint `json:"isTls" dc:"开启TLS:1=是,0=否"` + AuthType int `json:"authType" dc:"认证方式(1=Basic,2=AccessToken,3=证书)"` + AuthUser string `json:"authUser" dc:"认证用户"` + AuthPasswd string `json:"authPasswd" dc:"认证密码"` + AccessToken string `json:"accessToken" dc:"AccessToken"` + CertificateId int `json:"certificateId" dc:"证书ID"` + Stick string `json:"stick" dc:"粘包处理方式"` } func (s *Server) Open() { - _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionOpen, s.Id), nil) + _ = mqtt.Publish(consts.GetWrapperTopic(consts.DataBusServer, consts.ActionOpen, strconv.Itoa(s.Id)), nil) } // RegisterPacket 注册包 diff --git a/network/model/tunnel.go b/network/model/tunnel.go index e69f106..498532a 100644 --- a/network/model/tunnel.go +++ b/network/model/tunnel.go @@ -3,8 +3,8 @@ package model import ( "bytes" "encoding/hex" - "github.com/gogf/gf/v2/text/gregex" "regexp" + "strings" "time" ) @@ -36,8 +36,8 @@ type Tunnel struct { } type DefaultDevice struct { - Station int `json:"station"` - ProductId string `json:"product_id"` + Station int `json:"station"` + ProductKey string `json:"product_key"` } type TunnelEx struct { @@ -72,7 +72,15 @@ func (p *RegisterPacket) Check(buf []byte) (deviceKey string, checkOk bool) { if p.regex == nil { p.regex = regexp.MustCompile(p.Regex) } - match, _ := gregex.MatchString(p.Regex, string(buf)) + data := string(buf) + data = strings.ReplaceAll(data, "\n", "") + data = strings.ReplaceAll(data, "\r", "") + re := regexp.MustCompile(p.Regex) + match := re.FindStringSubmatch(data) + if match == nil { + return "", false + } + //todo 这里check获取的数据需要再次确认 return match[1], p.regex.Match(buf) } if p.Length > 0 { diff --git a/network/model/type.go b/network/model/type.go index 9d9c7c7..6901ae1 100644 --- a/network/model/type.go +++ b/network/model/type.go @@ -4,12 +4,12 @@ import ( "errors" "fmt" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/network/codebin" "math" + "sagooiot/network/codebin" "strings" ) -//DataType 数据类型 +// DataType 数据类型 type DataType int const ( @@ -27,7 +27,7 @@ const ( TypeDOUBLE ) -//Parse 解析类型 +// Parse 解析类型 func (dt *DataType) Parse(tp string) error { //var *dt DataType tp = tp[1 : len(tp)-1] @@ -77,7 +77,7 @@ func (dt *DataType) Parse(tp string) error { return nil } -//String 转化成字符串 +// String 转化成字符串 func (dt *DataType) String() string { var str string switch *dt { @@ -161,7 +161,7 @@ func (dt *DataType) Normalize(val interface{}) interface{} { } } -//Size 宽度 +// Size 宽度 func (dt *DataType) Size() int { var s int switch *dt { @@ -191,7 +191,7 @@ func (dt *DataType) Size() int { return s } -//Encode 编码 +// Encode 编码 func (dt *DataType) Encode(value interface{}, le bool, precision int) []byte { buf := make([]byte, 8) switch *dt { @@ -295,7 +295,7 @@ func (dt *DataType) Encode(value interface{}, le bool, precision int) []byte { return buf[:dt.Size()] } -//Decode 解码 +// Decode 解码 func (dt *DataType) Decode(buf []byte, le bool, precision int) (val interface{}, err error) { //避免越界访问 if len(buf) < dt.Size() { @@ -401,12 +401,12 @@ func (dt *DataType) Decode(buf []byte, le bool, precision int) (val interface{}, return } -//MarshalJSON 序列化 +// MarshalJSON 序列化 func (dt *DataType) MarshalJSON() ([]byte, error) { return []byte(`"` + dt.String() + `"`), nil } -//UnmarshalJSON 解析 +// UnmarshalJSON 解析 func (dt *DataType) UnmarshalJSON(buf []byte) error { return dt.Parse(string(buf)) } diff --git a/network/network.go b/network/network.go index fa288dd..4eab094 100644 --- a/network/network.go +++ b/network/network.go @@ -2,11 +2,22 @@ package network import ( "context" - "github.com/sagoo-cloud/sagooiot/network/core" + "sagooiot/network/core" + "sagooiot/network/core/device" + "sagooiot/network/core/server" + "sagooiot/network/core/tunnel" ) +var reloadNetWorkFunc = []func(ctx context.Context) error{ + // 开启主题订阅 + core.StartSubscriber, + device.LoadDevices, + tunnel.LoadTunnels, + server.LoadServers, +} + func ReloadNetwork(c context.Context) (err error) { - for _, f := range []func(ctx context.Context) error{core.StartSubscriber, core.LoadDevices, core.LoadTunnels, core.LoadServers} { + for _, f := range reloadNetWorkFunc { if err = f(c); err != nil { return err } diff --git a/network/pkg/cron/cron.go b/network/pkg/cron/cron.go deleted file mode 100644 index 8806d25..0000000 --- a/network/pkg/cron/cron.go +++ /dev/null @@ -1,70 +0,0 @@ -package cron - -import ( - "github.com/go-co-op/gocron" - "time" -) - -// Scheduler 调度器 -var Scheduler *gocron.Scheduler - -func init() { - Scheduler = gocron.NewScheduler(time.Local) // time.UTC - Scheduler.StartAsync() -} - -//是否是使用单一协程??? 是则要改成协程池??? - -// Schedule 创建任务 -func Schedule(crontab string, fn func()) (*Job, error) { - job, err := Scheduler.Cron(crontab).Do(fn) - if err != nil { - return nil, err - } - return &Job{job: job}, nil -} - -// Interval 创建周期任务 -func Interval(interval int, fn func()) (*Job, error) { - job, err := Scheduler.Every(interval).Milliseconds().Do(fn) - if err != nil { - return nil, err - } - return &Job{job: job}, nil -} - -// Clock 创建每日任务 -func Clock(hours int, minutes int, fn func()) (*Job, error) { - job, err := Scheduler.Every(1).Day().At(hours).Hours().At(minutes).Minutes().Do(fn) - if err != nil { - return nil, err - } - return &Job{job: job}, nil -} - -// ClockWithWeekdays 创建每日任务 -func ClockWithWeekdays(hours int, minutes int, weekdays []time.Weekday, fn func()) (*Job, error) { - s := Scheduler.Every(1).Day().At(hours).Hours().At(minutes).Minutes() - if len(weekdays) > 0 { - s.Weeks() - for _, w := range weekdays { - s.Weekday(w) - } - } - - job, err := s.Do(fn) - if err != nil { - return nil, err - } - return &Job{job: job}, nil -} - -// Job 任务 -type Job struct { - job *gocron.Job -} - -// Cancel 取消任务 -func (j *Job) Cancel() { - Scheduler.Remove(j.job) -} diff --git a/network/pkg/mqttclient/mqtt.go b/network/pkg/mqttclient/mqtt.go deleted file mode 100644 index d40bcbe..0000000 --- a/network/pkg/mqttclient/mqtt.go +++ /dev/null @@ -1,51 +0,0 @@ -package mqttclient - -import ( - "context" - mqtt "github.com/eclipse/paho.mqtt.golang" - "github.com/gogf/gf/v2/frame/g" -) - -type MqttConf struct { - Addr string - ClientId string - UserName string - Password string -} - -type MqttWrapperClient struct { - c mqtt.Client -} - -func InitMqtt(c *MqttConf) (*MqttWrapperClient, error) { - opts := mqtt.NewClientOptions() - opts.AddBroker(c.Addr) - opts.SetClientID(c.ClientId) - opts.SetUsername(c.UserName) - opts.SetPassword(c.Password) - mqttClient := mqtt.NewClient(opts) - if token := mqttClient.Connect(); token.Wait() && token.Error() != nil { - return nil, token.Error() - } else { - return &MqttWrapperClient{mqttClient}, nil - } -} - -func (m *MqttWrapperClient) Close() { - m.c.Disconnect(uint(250)) -} - -func (m *MqttWrapperClient) Publish(topic string, payload []byte) error { - pubToken := m.c.Publish(topic, 2, false, payload) - return pubToken.Error() -} - -func (m *MqttWrapperClient) Subscribe(ctx context.Context, topic string, h mqtt.MessageHandler) (err error) { - if subErr := m.c.Subscribe(topic, 2, h); subErr.Error() != nil { - g.Log().Errorf(ctx, "subscribe error: %s", subErr.Error()) - return subErr.Error() - } else { - return nil - } - -} diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go new file mode 100644 index 0000000..ad2a6f9 --- /dev/null +++ b/pkg/cache/cache.go @@ -0,0 +1,55 @@ +package cache + +import ( + "context" + "sagooiot/pkg/cache/file" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/os/gfile" +) + +// cache 缓存驱动 +var cache *gcache.Cache + +// Instance 缓存实例 +func Instance() *gcache.Cache { + if cache == nil { + panic("cache uninitialized.") + } + return cache +} + +// SetAdapter 设置缓存适配器 +func SetAdapter(ctx context.Context) { + var cacheAdapter gcache.Adapter + adapter := g.Cfg().MustGet(ctx, "cache.adapter").String() + fileDir := g.Cfg().MustGet(ctx, "cache.fileDir").String() + + switch adapter { + case "redis": + cacheAdapter = gcache.NewAdapterRedis(g.Redis()) + case "file": + if fileDir == "" { + g.Log().Fatal(ctx, "file path must be configured for file caching.") + return + } + + if !gfile.Exists(fileDir) { + if err := gfile.Mkdir(fileDir); err != nil { + g.Log().Fatalf(ctx, "failed to create the cache directory. procedure, err:%+v", err) + return + } + } + cacheAdapter = file.NewAdapterFile(fileDir) + default: + cacheAdapter = gcache.NewAdapterMemory() + } + + // 数据库缓存,默认和通用缓冲驱动一致,如果你不想使用默认的,可以自行调整 + g.DB().GetCache().SetAdapter(cacheAdapter) + + // 通用缓存 + cache = gcache.New() + cache.SetAdapter(cacheAdapter) +} diff --git a/pkg/cache/file/file.go b/pkg/cache/file/file.go new file mode 100644 index 0000000..927ee8b --- /dev/null +++ b/pkg/cache/file/file.go @@ -0,0 +1,434 @@ +package file + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/util/gconv" + "os" + "path/filepath" + "sync" + "time" +) + +type ( + // AdapterFile gcache适配器使用文件的实现 + AdapterFile struct { + dir string + mutex sync.RWMutex // 添加互斥锁以支持并发操作 + } + + fileContent struct { + Duration int64 `json:"duration"` + Data interface{} `json:"data,omitempty"` + } +) + +const perm = 0o666 //设置文件所有者、同用户组成员和其他用户都可以读写该文件,但都不能执。 + +var ( + CacheExpiredErr = errors.New("cache expired") +) + +// NewAdapterFile creates and returns a new memory cache object. +func NewAdapterFile(dir string) gcache.Adapter { + return &AdapterFile{ + dir: dir, + } +} + +func (c *AdapterFile) Set(ctx context.Context, key interface{}, value interface{}, lifeTime time.Duration) (err error) { + c.mutex.Lock() // 加锁 + defer c.mutex.Unlock() // 解锁 + fileKey := gconv.String(key) + if value == nil || lifeTime < 0 { + return c.Delete(fileKey) + } + return c.Save(fileKey, gconv.String(value), lifeTime) +} + +// SetMap 批量设置多个键值对,并且每个键值对的有效期是一样的 +func (c *AdapterFile) SetMap(ctx context.Context, data map[interface{}]interface{}, duration time.Duration) (err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + for key, value := range data { + fileKey := gconv.String(key) + if err = c.Save(fileKey, gconv.String(value), duration); err != nil { + return err + } + } + return nil +} + +// SetIfNotExist 在指定的键不存在时设置其值 +func (c *AdapterFile) SetIfNotExist(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (ok bool, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + fileKey := gconv.String(key) + if c.Has(fileKey) { + return false, nil + } + + err = c.Save(fileKey, gconv.String(value), duration) + return true, err +} + +// SetIfNotExistFunc 在指定的键不存在时,通过一个函数来生成值并设置它 +func (c *AdapterFile) SetIfNotExistFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + fileKey := gconv.String(key) + if c.Has(fileKey) { + return false, nil + } + + value, err := f(ctx) + if err != nil { + return false, err + } + err = c.Save(fileKey, gconv.String(value), duration) + return true, err +} + +// SetIfNotExistFuncLock 与 SetIfNotExistFunc 类似,但它在调用生成值的函数时提供了额外的锁机制,以避免在生成值期间的并发问题。 +func (c *AdapterFile) SetIfNotExistFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (ok bool, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + fileKey := gconv.String(key) + if c.Has(fileKey) { + return false, nil + } + + // 在这里加锁是为了确保在值生成期间不会有并发的写入操作 + value, err := f(ctx) + if err != nil { + return false, err + } + err = c.Save(fileKey, gconv.String(value), duration) + return true, err +} + +func (c *AdapterFile) Get(ctx context.Context, key interface{}) (*gvar.Var, error) { + fetch, err := c.Fetch(gconv.String(key)) + if err != nil { + return nil, err + } + return gvar.New(fetch), nil +} + +func (c *AdapterFile) GetOrSet(ctx context.Context, key interface{}, value interface{}, duration time.Duration) (result *gvar.Var, err error) { + result, err = c.Get(ctx, key) + if err != nil && !errors.Is(err, CacheExpiredErr) { + return nil, err + } + if result.IsNil() { + return gvar.New(value), c.Set(ctx, key, value, duration) + } + return +} + +func (c *AdapterFile) GetOrSetFunc(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) { + v, err := c.Get(ctx, key) + if err != nil && !errors.Is(err, CacheExpiredErr) { + return nil, err + } + if v.IsNil() { + value, err := f(ctx) + if err != nil { + return nil, err + } + if value == nil { + return nil, nil + } + return gvar.New(value), c.Set(ctx, key, value, duration) + } else { + return v, nil + } +} + +func (c *AdapterFile) GetOrSetFuncLock(ctx context.Context, key interface{}, f gcache.Func, duration time.Duration) (result *gvar.Var, err error) { + return c.GetOrSetFunc(ctx, key, f, duration) +} + +func (c *AdapterFile) Contains(ctx context.Context, key interface{}) (bool, error) { + return c.Has(gconv.String(key)), nil +} + +func (c *AdapterFile) Size(ctx context.Context) (size int, err error) { + return 0, nil +} + +// Data 获取所有缓存的键值对 +func (c *AdapterFile) Data(ctx context.Context) (data map[interface{}]interface{}, err error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + // 初始化返回的数据结构 + data = make(map[interface{}]interface{}) + + // 读取文件目录中的所有缓存文件 + files, err := os.ReadDir(c.dir) + if err != nil { + return nil, err + } + + // 遍历缓存文件,提取数据 + for _, file := range files { + content, err := c.read(file.Name()) + if err != nil { + continue // 忽略无法读取的文件 + } + data[file.Name()] = content.Data + } + return data, nil +} + +// Keys 获取所有缓存的键 +func (c *AdapterFile) Keys(ctx context.Context) (keys []interface{}, err error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + files, err := os.ReadDir(c.dir) + if err != nil { + return nil, err + } + + keys = make([]interface{}, 0, len(files)) + for _, file := range files { + keys = append(keys, file.Name()) + } + return keys, nil +} + +// Values 获取所有缓存的值 +func (c *AdapterFile) Values(ctx context.Context) (values []interface{}, err error) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + files, err := os.ReadDir(c.dir) + if err != nil { + return nil, err + } + + values = make([]interface{}, 0, len(files)) + for _, file := range files { + content, err := c.read(file.Name()) + if err != nil { + continue // 忽略无法读取的文件 + } + values = append(values, content.Data) + } + return values, nil +} + +// Update 更新一个指定键的缓存值,如果这个键不存在,则返回错误 +func (c *AdapterFile) Update(ctx context.Context, key interface{}, value interface{}) (oldValue *gvar.Var, exist bool, err error) { + c.mutex.Lock() + defer c.mutex.Unlock() + + fileKey := gconv.String(key) + content, err := c.read(fileKey) + if err != nil { + return nil, false, err + } + if content == nil { + return nil, false, nil + } + + oldValue = gvar.New(content.Data) + err = c.Save(fileKey, gconv.String(value), time.Duration(content.Duration)*time.Second) + return oldValue, true, err +} + +func (c *AdapterFile) UpdateExpire(ctx context.Context, key interface{}, duration time.Duration) (oldDuration time.Duration, err error) { + var ( + v *gvar.Var + oldTTL int64 + fileKey = gconv.String(key) + ) + // TTL. + expire, err := c.GetExpire(ctx, fileKey) + if err != nil { + return + } + oldTTL = int64(expire) + if oldTTL == -2 { + // It does not exist. + return + } + oldDuration = time.Duration(oldTTL) * time.Second + // DEL. + if duration < 0 { + err = c.Delete(fileKey) + return + } + v, err = c.Get(ctx, fileKey) + if err != nil { + return + } + err = c.Set(ctx, fileKey, v.Val(), duration) + return +} + +func (c *AdapterFile) GetExpire(ctx context.Context, key interface{}) (time.Duration, error) { + content, err := c.read(gconv.String(key)) + if err != nil { + return -1, nil + } + + if content.Duration <= time.Now().Unix() { + return -1, nil + } + return time.Duration(time.Now().Unix()-content.Duration) * time.Second, nil +} + +func (c *AdapterFile) Remove(ctx context.Context, keys ...interface{}) (lastValue *gvar.Var, err error) { + if len(keys) == 0 { + return nil, nil + } + // Retrieves the last key value. + if lastValue, err = c.Get(ctx, gconv.String(keys[len(keys)-1])); err != nil { + return nil, err + } + // Deletes all given keys. + err = c.DeleteMulti(gconv.Strings(keys)...) + return +} + +func (c *AdapterFile) Clear(ctx context.Context) error { + return c.Flush() +} + +func (c *AdapterFile) Close(ctx context.Context) error { + return nil +} + +func (c *AdapterFile) createName(key string) string { + h := sha256.New() + _, _ = h.Write([]byte(key)) + hash := hex.EncodeToString(h.Sum(nil)) + return filepath.Join(c.dir, fmt.Sprintf("%s.cache", hash)) +} + +func (c *AdapterFile) read(key string) (*fileContent, error) { + rp := gfile.RealPath(c.createName(key)) + if rp == "" { + return nil, nil + } + + value, err := os.ReadFile(rp) + if err != nil { + return nil, err + } + + content := &fileContent{} + if err := json.Unmarshal(value, content); err != nil { + return nil, err + } + + if content.Duration == 0 { + return content, nil + } + + if content.Duration <= time.Now().Unix() { + _ = c.Delete(key) + return nil, CacheExpiredErr + } + return content, nil +} + +// Has checks if the cached key exists into the File storage +func (c *AdapterFile) Has(key string) bool { + fc, err := c.read(key) + return err == nil && fc != nil +} + +// Delete the cached key from File storage +func (c *AdapterFile) Delete(key string) error { + _, err := os.Stat(c.createName(key)) + if err != nil && os.IsNotExist(err) { + return nil + } + return os.Remove(c.createName(key)) +} + +// DeleteMulti the cached key from File storage +func (c *AdapterFile) DeleteMulti(keys ...string) (err error) { + for _, key := range keys { + if err = c.Delete(key); err != nil { + return + } + } + return +} + +// Fetch retrieves the cached value from key of the File storage +func (c *AdapterFile) Fetch(key string) (interface{}, error) { + content, err := c.read(key) + if err != nil { + return nil, err + } + + if content == nil { + return nil, nil + } + return content.Data, nil +} + +// FetchMulti retrieve multiple cached values from keys of the File storage +func (c *AdapterFile) FetchMulti(keys []string) map[string]interface{} { + result := make(map[string]interface{}) + for _, key := range keys { + if value, err := c.Fetch(key); err == nil { + result[key] = value + } + } + return result +} + +// Flush removes all cached keys of the File storage +func (c *AdapterFile) Flush() error { + dir, err := os.Open(c.dir) + if err != nil { + return err + } + + defer func() { + _ = dir.Close() + }() + + names, _ := dir.Readdirnames(-1) + + for _, name := range names { + _ = os.Remove(filepath.Join(c.dir, name)) + } + return nil +} + +// Save a value in File storage by key +func (c *AdapterFile) Save(key string, value string, lifeTime time.Duration) error { + duration := int64(0) + + if lifeTime > 0 { + duration = time.Now().Unix() + int64(lifeTime.Seconds()) + } + + content := &fileContent{duration, value} + + data, err := json.Marshal(content) + if err != nil { + return err + } + + err = os.WriteFile(c.createName(key), data, perm) + return err +} diff --git a/pkg/channelx/aggregator.go b/pkg/channelx/aggregator.go new file mode 100644 index 0000000..13e1cab --- /dev/null +++ b/pkg/channelx/aggregator.go @@ -0,0 +1,217 @@ +package channelx + +import ( + "log" + "runtime" + "sync" + "time" +) + +// Aggregator 聚合器结构体 +type Aggregator struct { + option AggregatorOption + wg sync.WaitGroup + quit chan struct{} + eventQueue chan interface{} + batchProcessor BatchProcessFunc + pool *sync.Pool +} + +// AggregatorOption 聚合器选项 +type AggregatorOption struct { + BatchSize int + Workers int + ChannelBufferSize int + LingerTime time.Duration + ErrorHandler ErrorHandlerFunc + Logger *log.Logger +} + +// BatchProcessFunc 批处理函数类型 +type BatchProcessFunc func([]interface{}) error + +// SetAggregatorOptionFunc 聚合器选项设置函数类型 +type SetAggregatorOptionFunc func(option *AggregatorOption) + +// ErrorHandlerFunc 错误处理函数类型 +type ErrorHandlerFunc func(err error, items []interface{}, batchProcessFunc BatchProcessFunc, aggregator *Aggregator) + +// NewAggregator 创建新的聚合器实例 +func NewAggregator(batchProcessor BatchProcessFunc, optionFuncs ...SetAggregatorOptionFunc) *Aggregator { + option := AggregatorOption{ + BatchSize: 8, + Workers: runtime.NumCPU(), + LingerTime: 1 * time.Minute, + } + + for _, optionFunc := range optionFuncs { + optionFunc(&option) + } + + if option.ChannelBufferSize < option.Workers { + option.ChannelBufferSize = option.Workers + } + + pool := &sync.Pool{ + New: func() interface{} { + return make([]interface{}, 0, option.BatchSize) + }, + } + + return &Aggregator{ + eventQueue: make(chan interface{}, option.ChannelBufferSize), + option: option, + quit: make(chan struct{}), + batchProcessor: batchProcessor, + pool: pool, + } +} + +// TryEnqueue 尝试入队一个项目,非阻塞 +func (agt *Aggregator) TryEnqueue(item interface{}) bool { + select { + case agt.eventQueue <- item: + return true + default: + if agt.option.Logger != nil { + agt.option.Logger.Println("Aggregator: 事件队列已满,尝试重新安排") + } + runtime.Gosched() // 让出CPU时间片 + select { + case agt.eventQueue <- item: + return true + default: + if agt.option.Logger != nil { + agt.option.Logger.Printf("Aggregator: 事件队列仍然已满,并且跳过了 %+v \n", item) + } + return false + } + } +} + +// Enqueue 入队一个项目,会阻塞直到有空间 +func (agt *Aggregator) Enqueue(item interface{}) { + select { + case agt.eventQueue <- item: + case <-agt.quit: + if agt.option.Logger != nil { + agt.option.Logger.Println("Aggregator: 正在停止,入队操作被中断") + } + } +} + +// EnqueueWithRetry 入队一个项目,会重试直到成功或者达到最大重试次数 +func (agt *Aggregator) EnqueueWithRetry(item interface{}, maxRetries int, backoff time.Duration) bool { + for i := 0; i < maxRetries; i++ { + if agt.TryEnqueue(item) { + return true // 入队成功 + } + time.Sleep(backoff) // 等待一段时间后重试 + backoff *= 2 // 指数退避 + } + return false // 最终尝试失败 +} + +// Start 启动聚合器 +func (agt *Aggregator) Start() { + agt.wg.Add(agt.option.Workers) + for i := 0; i < agt.option.Workers; i++ { + go agt.work() + } +} + +// Stop 停止聚合器 +func (agt *Aggregator) Stop() { + close(agt.quit) + agt.wg.Wait() +} + +// SafeStop 安全停止聚合器,确保所有项目都被处理 +func (agt *Aggregator) SafeStop() { + close(agt.quit) + agt.wg.Wait() // 等待所有工作协程退出 +} + +func (agt *Aggregator) work() { + defer agt.wg.Done() + + batch := agt.pool.Get().([]interface{}) + defer agt.pool.Put(batch[:0]) + + lingerTimer := time.NewTimer(agt.option.LingerTime) + defer lingerTimer.Stop() + + for { + select { + case item := <-agt.eventQueue: + batch = append(batch, item) + if len(batch) == cap(batch) { + agt.processBatch(batch) + batch = batch[:0] // 清空切片 + } + case <-lingerTimer.C: + if len(batch) > 0 { + agt.processBatch(batch) + batch = batch[:0] // 清空切片 + } + lingerTimer.Reset(agt.option.LingerTime) + case <-agt.quit: + if len(batch) > 0 { + agt.processBatch(batch) + } + return // 退出工作协程 + } + } +} + +func (agt *Aggregator) processBatch(items []interface{}) { + defer agt.wg.Add(1) + defer agt.wg.Done() + if err := agt.batchProcessor(items); err != nil { + if agt.option.Logger != nil { + agt.option.Logger.Println("Aggregator: 处理批次时发生错误") + } + if agt.option.ErrorHandler != nil { + agt.option.ErrorHandler(err, items, agt.batchProcessor, agt) + } + } else if agt.option.Logger != nil { + agt.option.Logger.Printf("Aggregator: 成功处理了%d个项目。\n", len(items)) + } +} + +// 示例: 设置聚合器选项的函数 +func WithBatchSize(batchSize int) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.BatchSize = batchSize + } +} + +func WithWorkers(workers int) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.Workers = workers + } +} + +func WithChannelBufferSize(size int) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.ChannelBufferSize = size + } +} + +func WithLingerTime(duration time.Duration) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.LingerTime = duration + } +} + +func WithLogger(logger *log.Logger) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.Logger = logger + } +} + +func WithErrorHandler(handler ErrorHandlerFunc) SetAggregatorOptionFunc { + return func(option *AggregatorOption) { + option.ErrorHandler = handler + } +} diff --git a/pkg/channelx/aggregator_test.go b/pkg/channelx/aggregator_test.go new file mode 100644 index 0000000..e02e5ee --- /dev/null +++ b/pkg/channelx/aggregator_test.go @@ -0,0 +1,52 @@ +package channelx + +import ( + "log" + "os" + "testing" + "time" +) + +// mockBatchProcessFunc 是一个模拟的批处理函数,用于测试 +func mockBatchProcessFunc(items []interface{}) error { + // 在这里简单模拟处理逻辑,实际使用中应替换为具体的业务逻辑 + log.Println("批量处理:", items) + return nil +} + +// TestAggregator 测试Aggregator的基本行为 +func TestAggregator(t *testing.T) { + logger := log.New(os.Stdout, "AggregatorTest: ", log.Lshortfile) + batchSize := 5 // 批处理大小 + workers := 5 + channelBufferSize := 5000 // 通道缓冲区大小 + lingerTime := 100 * time.Millisecond + + // 创建聚合器实例 + aggregator := NewAggregator( + mockBatchProcessFunc, + WithBatchSize(batchSize), + WithWorkers(workers), + WithChannelBufferSize(channelBufferSize), + WithLingerTime(lingerTime), + WithLogger(logger), + ) + + // 开始聚合器 + aggregator.Start() + + // 模拟入队操作 + itemsToEnqueue := 2000 + for i := 0; i < itemsToEnqueue+1; i++ { + if !aggregator.TryEnqueue(i) { + t.Logf("Failed to enqueue item: %d", i) + } + } + + // 等待足够的时间,以确保所有项目都被处理 + time.Sleep(2 * time.Second) + + // 安全停止聚合器 + aggregator.SafeStop() + +} diff --git a/pkg/dcache/dcache.go b/pkg/dcache/dcache.go new file mode 100644 index 0000000..4e13409 --- /dev/null +++ b/pkg/dcache/dcache.go @@ -0,0 +1,174 @@ +package dcache + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "strings" +) + +// GetDeviceStatus 获取指定的设备状态 +func GetDeviceStatus(ctx context.Context, deviceKey string) (res int) { + data, err := cache.Instance().Get(ctx, consts.DeviceStatusPrefix+deviceKey) + if err != nil || data == nil { + return 1 + } + if data.Val() != nil { + return 2 + } + return 1 +} + +// GetOnlineDeviceList 获取在线设备列表 +func GetOnlineDeviceList() (list g.Slice, err error) { + dataList, err := SearchKey(consts.DeviceStatusPrefix) + if err != nil { + return + } + for _, value := range dataList { + list = append(list, strings.TrimPrefix(value, consts.DeviceStatusPrefix)) + } + return +} + +// GetAllDeviceList 获取缓存中的所有设备列表 +func GetAllDeviceList() (outList []*model.DeviceOutput, err error) { + deviceKeyList, err := SearchKey(consts.DeviceDetailInfoPrefix) + if err != nil { + return + } + for _, key := range deviceKeyList { + data, err := cache.Instance().Get(context.Background(), key) + if err != nil || data.Val() == nil { + continue + } + var out model.DeviceOutput + if err = gconv.Scan(data.Val(), &out); err != nil { + continue + } + outList = append(outList, &out) + } + return +} + +// CountDeviceOnlineNum 统计在线设备数量 +func CountDeviceOnlineNum() (num int) { + data, err := SearchKey(consts.DeviceStatusPrefix) + if err != nil { + return 0 + } + num = len(data) + return +} + +// GetDeviceDetailInfo 获取设备详情缓存 +func GetDeviceDetailInfo(deviceKey string) (out *model.DeviceOutput, err error) { + data, err := cache.Instance().Get(context.Background(), consts.DeviceDetailInfoPrefix+deviceKey) + if err != nil || data.Val() == nil { + return + } + if err = gconv.Scan(data.Val(), &out); err != nil { + return + } + + return +} + +// SetDeviceDetailInfo 设置设备详情缓存 +func SetDeviceDetailInfo(deviceKey string, data *model.DeviceOutput) (err error) { + if data == nil || data.Product == nil { + return + } + //设备启用后,更新缓存数据 + if data.Product.Metadata != "" { + _ = json.Unmarshal([]byte(data.Product.Metadata), &data.TSL) + data.Product.Metadata = "" + } + err = cache.Instance().Set(context.Background(), consts.DeviceDetailInfoPrefix+deviceKey, data, 0) + return +} + +// GetProductDetailInfo 获取产品详情缓存 +func GetProductDetailInfo(productKey string) (out *model.DetailProductOutput, err error) { + data, err := cache.Instance().Get(context.Background(), consts.ProductDetailInfoPrefix+productKey) + if err != nil || data.Val() == nil { + return + } + if err = gconv.Scan(data.Val(), &out); err != nil { + return + } + productDetailInfo, err := service.DevProduct().Detail(context.Background(), productKey) + if err != nil { + return + } + err = SetProductDetailInfo(productKey, productDetailInfo) + return +} + +// SetProductDetailInfo 设置产品详情缓存 +func SetProductDetailInfo(productKey string, data *model.DetailProductOutput) (err error) { + if data == nil { + return + } + //设备启用后,更新缓存数据 + if data.Metadata != "" { + _ = json.Unmarshal([]byte(data.Metadata), &data.TSL) + data.Metadata = "" + } + err = cache.Instance().Set(context.Background(), consts.ProductDetailInfoPrefix+productKey, data, 0) + return +} + +// SearchKey 搜索指定的key +func SearchKey(keyword string) (keys []string, err error) { + data, err := cache.Instance().Keys(context.Background()) + if err != nil { + return nil, err + } + for _, item := range data { + if strings.Contains(item.(string), keyword) { + keys = append(keys, item.(string)) + } + } + return +} + +// InitSystemConfig 初始化系统参数配置 +func InitSystemConfig(ctx context.Context) (err error) { + var req model.ConfigDoInput + req.PageSize = 10000 + _, configDataList, err := service.ConfigData().List(ctx, &req) + if err != nil { + return err + } + if configDataList != nil { + for _, configData := range configDataList { + err := cache.Instance().Set(ctx, consts.SystemConfigPrefix+configData.ConfigKey, configData, 0) + if err != nil { + continue + } + } + } + return +} + +// SetConfigByKey 设置系统参数配置 +func SetConfigByKey(ctx context.Context, configKey string, configValue any) (value any, err error) { + err = cache.Instance().Set(ctx, consts.SystemConfigPrefix+configKey, configValue, 0) + return +} + +// GetConfigByKey 获取系统参数配置 +func GetConfigByKey(ctx context.Context, key string) (value any, err error) { + cf, err := cache.Instance().Get(ctx, consts.SystemConfigPrefix+key) + if cf != nil && !cf.IsEmpty() { + err = gconv.Struct(cf.Val(), &value) + return + } + return +} diff --git a/pkg/dcache/dcache_test.go b/pkg/dcache/dcache_test.go new file mode 100644 index 0000000..9c84be7 --- /dev/null +++ b/pkg/dcache/dcache_test.go @@ -0,0 +1,37 @@ +package dcache + +import ( + "github.com/gogf/gf/v2/frame/g" + "testing" +) + +func TestCountDeviceOnlineNum(t *testing.T) { + treeObj := CountDeviceOnlineNum() + g.Dump(treeObj) +} + +func TestSearchKey(t *testing.T) { + data, err := SearchKey("DeviceDetailInfo:") + if err != nil { + t.Fatal(err) + } + g.Dump(data) +} + +func TestGetDeviceDetailInfo(t *testing.T) { + data, err := GetDeviceDetailInfo("t202201621") + if err != nil { + t.Fatal(err) + } + g.Dump(data) + +} + +func TestGetOnlineDeviceList(t *testing.T) { + data, err := GetOnlineDeviceList() + if err != nil { + t.Fatal(err) + } + g.Dump(data) + +} diff --git a/pkg/dcache/device_alarm.go b/pkg/dcache/device_alarm.go new file mode 100644 index 0000000..1b30358 --- /dev/null +++ b/pkg/dcache/device_alarm.go @@ -0,0 +1,31 @@ +package dcache + +import ( + "context" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/pkg/cache" +) + +// GetDeviceAlarm 基于产品key获取设备告警规则 +func GetDeviceAlarm(ctx context.Context, productKey string) (out []model.AlarmRuleOutput) { + data, err := cache.Instance().Get(ctx, consts.DeviceAlarmRulePrefix+productKey) + if err != nil || data.Val() == nil { + return + } + if err = gconv.Scan(data, &out); err != nil { + return + } + + return +} + +// SetDeviceAlarmRule 基于产品key设置设备告警规则 +func SetDeviceAlarmRule(ctx context.Context, productKey string, data []model.AlarmRuleOutput) (err error) { + if data == nil { + return + } + err = cache.Instance().Set(ctx, consts.DeviceAlarmRulePrefix+productKey, data, 0) + return +} diff --git a/pkg/dcache/device_data.go b/pkg/dcache/device_data.go new file mode 100644 index 0000000..0646557 --- /dev/null +++ b/pkg/dcache/device_data.go @@ -0,0 +1,133 @@ +package dcache + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/service" + "sagooiot/pkg/iotModel" +) + +// InertDeviceLog 插入设备日志 +func InertDeviceLog(ctx context.Context, logType, deviceKey string, obj interface{}) { + str, strIsOk := obj.(string) + content := str + if !strIsOk { + objStr, _ := json.Marshal(obj) + content = string(objStr) + } + // 向设备缓存数据库插入数据 + if err := DB().InsertData(context.Background(), deviceKey, iotModel.DeviceLog{ + Ts: gtime.Now(), + Device: deviceKey, + Type: logType, + Content: content, + }); err != nil { + g.Log().Debugf(ctx, "Failed to insert data: %v\n", err) + } +} + +// GetDeviceDetailData 获取设备解析后的详细数据 +func GetDeviceDetailData(ctx context.Context, deviceKey string) (res []map[string]iotModel.ReportPropertyNode) { + // 从设备缓存数据库获取数据 + dataList, err := DB().GetData(context.Background(), deviceKey) + if err != nil { + g.Log().Debugf(ctx, "Failed to get data: %v", err) + return + } + for _, data := range dataList { + if data == "" { + continue + } + var value = iotModel.DeviceLog{} + if err := json.Unmarshal([]byte(data), &value); err != nil { + g.Log().Debugf(ctx, "Failed to unmarshal data: %v", err) + } + + // 基于物模型解析数据 + tmp, err := service.DevTSLParse().ParseData(ctx, deviceKey, []byte(value.Content)) + if err != nil { + return + } + res = append(res, tmp) + } + return +} + +// GetDeviceDetailDataByLatest 获取设备解析后的最新一条数据 +func GetDeviceDetailDataByLatest(ctx context.Context, deviceKey string) (res iotModel.ReportPropertyData) { + // 从设备缓存数据库获取数据 + data, err := DB().GetDataByLatest(context.Background(), deviceKey) + if err != nil || data == "" { + g.Log().Debugf(ctx, "Failed to get data: %v", err) + return + } + + var value = iotModel.DeviceLog{} + if err := json.Unmarshal([]byte(data), &value); err != nil { + g.Log().Debugf(ctx, "Failed to unmarshal data: %v", err) + return + } + + // 基于物模型解析数据 + res, err = service.DevTSLParse().ParseData(ctx, deviceKey, []byte(value.Content)) + if err != nil { + g.Log().Debugf(ctx, "Failed to parse data: %v", err) + } + return +} + +// GetDeviceDetailDataByPage 按分页获取设备详细数据,分页参数: pageNum 为页码, pageSize 为每页数量 +func GetDeviceDetailDataByPage(ctx context.Context, deviceKey string, pageNum, pageSize int) (res []map[string]iotModel.ReportPropertyNode, total, currentPage int) { + // 获取 list 的名称 + listName := DeviceDataCachePrefix + deviceKey + + // 获取 list 的长度 + num, err := DB().client.LLen(context.Background(), listName).Result() + if err != nil { + fmt.Println(err) + return + } + total = int(num) + + if pageNum <= 0 { + pageNum = 1 + } + + if pageSize <= 0 { + pageSize = 10 + } + currentPage = pageNum + // 计算分页的起始位置和结束位置 + start := (pageNum - 1) * pageSize + end := start + pageSize + + if end > total { + end = total + } + + // 获取分页数据 + dataList, err := DB().client.LRange(context.Background(), listName, int64(start), int64(end)-1).Result() + if err != nil { + g.Log().Debugf(ctx, "Failed to get data: %v", err) + } + for _, data := range dataList { + if data == "" { + continue + } + var value = iotModel.DeviceLog{} + if err := json.Unmarshal([]byte(data), &value); err != nil { + g.Log().Debugf(ctx, "Failed to unmarshal data: %v", err) + } + + // 基于物模型解析数据 + tmp, err := service.DevTSLParse().ParseData(ctx, deviceKey, []byte(value.Content)) + if err != nil { + return + } + res = append(res, tmp) + } + return +} diff --git a/pkg/dcache/device_data_test.go b/pkg/dcache/device_data_test.go new file mode 100644 index 0000000..b082b71 --- /dev/null +++ b/pkg/dcache/device_data_test.go @@ -0,0 +1,129 @@ +package dcache + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "github.com/gogf/gf/v2/util/grand" + "log" + "sagooiot/pkg/iotModel" + "testing" + "time" +) + +// TestInsertData 插入数据示例 +func TestInsertBatchData(t *testing.T) { + ctx := context.Background() + //manager := GetRedisManager("localhost:6379") + // 插入数据示例 + // 创建DeviceLogData类型的数据 + var logList []interface{} + for i := 0; i < 3000+1; i++ { + devLog := iotModel.DeviceLog{ + Ts: gtime.Now(), + Device: "device001", + Type: "info", + Content: fmt.Sprintf("Log message %d", i), + } + logList = append(logList, devLog) + } + + if err := DB().InsertBatchData(ctx, "mylist222", logList); err != nil { + t.Error("Error inserting data:", err) + return + } +} + +// TestInsertData 插入数据示例 +func TestInsertData(t *testing.T) { + data := map[string]interface{}{ + "name": grand.S(6), + "age": grand.Intn(100), + "time": time.Now(), + } + t.Log(data) + // 插入单条数据 + if err := DB().InsertData(context.Background(), "userKey", data); err != nil { + log.Printf("Failed to insert data: %s\n", err) + } +} + +// TestGetData 获取数据示例 +func TestGetData(t *testing.T) { + ctx := context.Background() + // 获取数据示例 + result, err := DB().GetData(ctx, "mylist222") + if err != nil { + t.Error("Error getting data:", err) + return + } + var logs []iotModel.DeviceLog + if err := gconv.Scan(result, &logs); err != nil { + t.Error(err) + } + t.Log("记录数:", len(logs)) + for _, devLog := range logs { + fmt.Printf("Device: %s, Type: %s, Content: %s, Timestamp: %v\n", devLog.Device, devLog.Type, devLog.Content, devLog.Ts) + } + +} + +// TestListenForNewData 监听新数据示例 +func TestListenForNewData(t *testing.T) { + // 创建一个可取消的上下文 + ctx, cancel := context.WithCancel(context.Background()) + // 启动协程以监听 Redis key, 1秒钟检查一次 + var tmpData []interface{} + go DB().ListenForNewData(ctx, "userKey", func(data string) { + fmt.Println("处理新数据:", data) + tmpData = append(tmpData, data) + + }, 1*time.Second) + + //延迟落库处理 + ticker := time.NewTicker(time.Second * 30) + go func() { + for { + select { + case <-ctx.Done(): + //fmt.Println("延迟落库处理监听结束") + return + case <-ticker.C: + //fmt.Println("=====监听中=========") + //如果数据为空退出 + if len(tmpData) == 0 { + continue + } + fmt.Println(tmpData) + tmpData = nil + } + } + }() + + // 做一些操作,例如等待用户输入或达到某个条件 + time.Sleep(1600 * time.Second) // 示例:等待10秒 + + // 当需要停止监听时,调用 cancel 函数 + cancel() +} + +// TestGetDeviceDetailData 获取设备数据示例 +func TestGetDeviceDetailData(t *testing.T) { + // 获取数据示例 + resultList := GetDeviceDetailData(context.Background(), "t20221222") + for _, value := range resultList { + t.Log(value) + + } + t.Log("记录数:", len(resultList)) +} + +// TestGetDeviceDetailDataByPage 获取设备数据示例 +func TestGetDeviceDetailDataByPage(t *testing.T) { + dataList, total, currentPage := GetDeviceDetailDataByPage(context.Background(), "t20221222", 2, 10) + for _, d := range dataList { + t.Log(d) + } + t.Log("记录数:", len(dataList), total, currentPage) +} diff --git a/pkg/dcache/device_scene.go b/pkg/dcache/device_scene.go new file mode 100644 index 0000000..14194f3 --- /dev/null +++ b/pkg/dcache/device_scene.go @@ -0,0 +1 @@ +package dcache diff --git a/pkg/dcache/device_status.go b/pkg/dcache/device_status.go new file mode 100644 index 0000000..beb1895 --- /dev/null +++ b/pkg/dcache/device_status.go @@ -0,0 +1,133 @@ +package dcache + +import ( + "context" + "encoding/json" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "github.com/gogf/gf/v2/util/gconv" + "sagooiot/internal/consts" + "sagooiot/internal/model" + "sagooiot/internal/model/entity" + "sagooiot/internal/queues" + "sagooiot/internal/service" + "sagooiot/pkg/cache" + "sagooiot/pkg/iotModel" + "time" +) + +// UpdateStatus 更新设备状态 +func UpdateStatus(ctx context.Context, device *model.DeviceOutput) { + timeout := 0 + if device.OnlineTimeout > 0 { + timeout = device.OnlineTimeout + } + if timeout == 0 { + //设备超时时间 + defaultTimeout, err := service.ConfigData().GetConfigByKey(ctx, consts.DeviceDefaultTimeoutTime) + if err != nil || defaultTimeout == nil { + defaultTimeout = &entity.SysConfig{ + ConfigValue: "30", + } + } + timeout = gconv.Int(defaultTimeout.ConfigValue) + } + deviceStatus := GetDeviceStatus(ctx, device.Key) + + if deviceStatus == consts.DeviceStatueOnline { + //正常数据上报,更新设备在线的缓存时间 + _, err := cache.Instance().UpdateExpire(ctx, consts.DeviceStatusPrefix+device.Key, time.Duration(timeout+1)*time.Second) + if err != nil { + g.Log().Debug(ctx, device.Key, "更新设备在线的缓存时间失败") + } + + } else { + var deviceStatusLog = new(iotModel.DeviceStatusLog) + deviceStatusLog.Status = 2 + deviceStatusLog.Timestamp = time.Now() + deviceStatusLog.DeviceKey = device.Key + //设备首次上线,设置设备在线的缓存时间 + if err := cache.Instance().Set(ctx, consts.DeviceStatusPrefix+device.Key, deviceStatusLog, time.Duration(timeout)*time.Second); err != nil { + g.Log().Debug(ctx, device.Key, "设置设备在线的缓存时间失败") + } + + //添加延时下线消息判断 + go func(deviceKey string, timeOutSecond time.Duration) { + if err := online(ctx, device); err != nil { + g.Log().Debug(ctx, device.Key, "设备上线处理失败") + } + for { + ds := GetDeviceStatus(ctx, deviceKey) + if ds == consts.DeviceStatueOffline { //离线 + break + } + time.Sleep(timeOutSecond * time.Second) + } + + //设备下线 + err := offline(ctx, device) + if err != nil { + return + } + }(device.Key, time.Duration(timeout)*time.Second) + } +} + +// pushDeviceStatus 推送设备状态 +func pushDeviceStatus(deviceKey string, status int) { + var deviceStatusLog = new(iotModel.DeviceStatusLog) + deviceStatusLog.Status = status + deviceStatusLog.Timestamp = time.Now() + deviceStatusLog.DeviceKey = deviceKey + data, _ := json.Marshal(deviceStatusLog) + err := queues.DeviceStatusInfoUpdateWorker.Push(context.Background(), consts.QueueDeviceStatusInfoUpdate, data, 10) + if err != nil { + g.Log().Debug(context.Background(), "Push DeviceStatusInfoUpdateWorker: %v", err) + } +} + +// online 设备上线 +func online(ctx context.Context, device *model.DeviceOutput) (err error) { + //插入设备上线日志 + InertDeviceLog(ctx, consts.MsgTypeOnline, device.Key, iotModel.DeviceOnlineMessage{ + Timestamp: time.Now().UnixMilli(), + Desc: "", + }) + pushDeviceStatus(device.Key, 2) + + //告警处理 + go func() { + // 上线告警提醒 + data := iotModel.ReportStatusData{ + Status: "online", + CreateTime: gtime.Now().Unix(), + } + if err == nil { + err = service.AlarmRule().Check(ctx, device.Product.Key, device.Key, consts.AlarmTriggerTypeOnline, data) + if err != nil { + g.Log().Errorf(ctx, "告警检测失败: %s", err.Error()) + } + } + }() + + return +} + +// offline 设备下线 +func offline(ctx context.Context, device *model.DeviceOutput) (err error) { + InertDeviceLog(ctx, consts.MsgTypeOffline, device.Key, iotModel.DeviceOfflineMessage{ + Timestamp: time.Now().UnixMilli(), + Desc: "", + }) + pushDeviceStatus(device.Key, 1) + + // 离线告警提醒 + data := iotModel.ReportStatusData{ + Status: "offline", + CreateTime: gtime.Now().Unix(), + } + if err == nil { + err = service.AlarmRule().Check(ctx, device.ProductKey, device.Key, consts.AlarmTriggerTypeOffline, data) + } + return +} diff --git a/pkg/dcache/redisManager.go b/pkg/dcache/redisManager.go new file mode 100644 index 0000000..0342d67 --- /dev/null +++ b/pkg/dcache/redisManager.go @@ -0,0 +1,311 @@ +package dcache + +import ( + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/redis/go-redis/v9" + "strings" + "sync" + "time" +) + +const DeviceDataCachePrefix = "deviceCacheData:" + +// RedisManager 管理Redis操作和连接池 +type RedisManager struct { + client *redis.Client + recordDuration time.Duration // 记录的有效时间 + recordLimit int64 // 记录的条数限制 +} + +type redisOptions struct { + Addr string // Redis服务器地址 + DB int // Redis数据库 + Password string // Redis密码 + PoolSize int // 连接池大小 + RecordDuration string // 记录的有效时间 + RecordLimit int64 // 记录的条数限制 +} + +var ( + managerInstance *RedisManager + once sync.Once +) + +// DataProcessor 是一个处理新数据的函数类型 +type DataProcessor func(data string) + +// DB 用于全局获取RedisManager实例 +func DB() *RedisManager { + once.Do(func() { + // 从配置文件获取redis的ip以及db + address := g.Cfg().MustGet(context.Background(), "redis.default.address", "localhost:6379").String() + db := g.Cfg().MustGet(context.Background(), "redis.default.db", "0").Int() + password := g.Cfg().MustGet(context.Background(), "redis.default.pass", "").String() + poolSize := g.Cfg().MustGet(context.Background(), "system.deviceCacheData.poolSize", "500").Int() + recordDuration := g.Cfg().MustGet(context.Background(), "system.deviceCacheData.recordDuration", "10m").String() + recordLimit := g.Cfg().MustGet(context.Background(), "system.deviceCacheData.recordLimit", "1000").Int64() + + // 从配置文件获取redis的ip以及db + options := redisOptions{ + Addr: address, + DB: db, + Password: password, // 如果需要密码 + PoolSize: poolSize, // 设置连接池大小 + RecordDuration: recordDuration, // 记录的有效时间 + RecordLimit: recordLimit, // 记录的条数限制 + } + managerInstance = getRedisManager(options) + }) + return managerInstance + +} + +// GetRedisManager 单例模式获取RedisManager实例 +func getRedisManager(options redisOptions) *RedisManager { + var client *redis.Client + var err error + + recordDuration, err := time.ParseDuration(options.RecordDuration) + if err != nil { + recordDuration = 10 * time.Minute // 或设置一个默认值 + fmt.Println("Invalid RecordDuration format, setting to default:", recordDuration) + } + + for { // 按秒持续连接尝试 + client = redis.NewClient(&redis.Options{ + Addr: options.Addr, + DB: options.DB, + Password: options.Password, + PoolSize: options.PoolSize, + ClientName: "DeviceData", // 设置连接名称 + }) + // 尝试 Ping 操作以检查连接 + _, err = client.Ping(context.Background()).Result() + if err == nil { + break // 如果成功,则中断循环 + } + + // 如果连接失败,等待一段时间后重试 + fmt.Printf("Failed to connect to Redis: %v. Retrying in 1 second...\n", err) + time.Sleep(1 * time.Second) + } + + // 如果经过重试后仍然失败,处理错误(或退出程序) + if err != nil { + // 这里可以记录错误、返回nil或退出程序 + fmt.Printf("Failed to connect to Redis after retries: %v\n", err) + return nil + } + + return &RedisManager{ + client: client, + recordDuration: recordDuration, + recordLimit: options.RecordLimit, + } +} + +// GetClient 获取Redis客户端 +func (r *RedisManager) GetClient() *redis.Client { + return r.client +} + +// InsertBatchData 批量插入数据 +func (r *RedisManager) InsertBatchData(ctx context.Context, key string, data []interface{}) error { + pipe := r.client.Pipeline() + for _, item := range data { + serializedValue, err := json.Marshal(item) + if err != nil { + return err + } + pipe.LPush(ctx, DeviceDataCachePrefix+key, serializedValue) + } + + // 设置列表长度限制 + pipe.LTrim(ctx, DeviceDataCachePrefix+key, 0, r.recordLimit-1) + + // 设置过期时间 + pipe.Expire(ctx, DeviceDataCachePrefix+key, r.recordDuration) + + // 执行所有命令 + _, err := pipe.Exec(ctx) + return err +} + +// InsertData 插入单条数据 +func (r *RedisManager) InsertData(ctx context.Context, key string, data interface{}) error { + serializedValue, err := json.Marshal(data) + if err != nil { + return err + } + + err = r.client.LPush(ctx, DeviceDataCachePrefix+key, serializedValue).Err() + if err != nil { + return err + } + + // 设置列表长度限制 + r.client.LTrim(ctx, DeviceDataCachePrefix+key, 0, r.recordLimit-1) + + // 设置过期时间 + r.client.Expire(ctx, DeviceDataCachePrefix+key, r.recordDuration) + + return nil +} + +// GetData 获取最新的数据 +func (r *RedisManager) GetData(ctx context.Context, key string) ([]string, error) { + return r.client.LRange(ctx, DeviceDataCachePrefix+key, 0, r.recordLimit-1).Result() +} + +// GetDataByLatest 获取最新的一条数据 +func (r *RedisManager) GetDataByLatest(ctx context.Context, key string) (string, error) { + return r.client.LIndex(ctx, DeviceDataCachePrefix+key, -1).Result() +} + +// GetDataByPage 按分页获取数据,增加按字段内容搜索和时间区间搜索 +func GetDataByPage(ctx context.Context, deviceKey string, pageNum, pageSize int, types, dateRange []string) (res []string, total, currentPage int, err error) { + listName := DeviceDataCachePrefix + deviceKey + + // 先获取全部数据 + allData, err := DB().client.LRange(ctx, listName, 0, -1).Result() + if err != nil { + fmt.Println(err) + return + } + + if dateRange == nil { + dateRange = make([]string, 2) + dateRange[0] = "1970-01-01" + dateRange[1] = time.Now().Format("2006-01-02") + } + + startDate, endDate := parseDateRange(dateRange) + + var tmpDataList []string + for _, item := range allData { + if matchesTypes(item, types) && inDateRange(item, startDate, endDate) { + tmpDataList = append(tmpDataList, item) + } + } + + total = len(tmpDataList) + + if pageNum <= 0 { + pageNum = 1 + } + + if pageSize <= 0 { + pageSize = 10 + } + currentPage = pageNum + res, _ = getPage(tmpDataList, pageNum, pageSize) + + return +} + +// getPage 进行分页 +func getPage(data []string, pageNum, pageSize int) ([]string, int) { + if pageNum < 1 || pageSize < 1 { + return []string{}, 0 + } + // 计算总页数 + total := len(data) / pageSize + if len(data)%pageSize != 0 { + total += 1 + } + + // 检查页码是否超出范围 + if pageNum > total { + return []string{}, total + } + + // 计算分页的起始位置和结束位置 + start := (pageNum - 1) * pageSize + end := start + pageSize + + // 调整结束位置以防止越界 + if end > len(data) { + end = len(data) + } + + // 截取分页数据 + res := data[start:end] + + return res, total +} + +// matchesTypes 检查数据是否匹配指定的类型 +func matchesTypes(data string, types []string) bool { + for _, t := range types { + if strings.Contains(data, t) { + return true + } + } + return len(types) == 0 +} + +// inDateRange 检查数据是否在指定的日期范围内 +func inDateRange(data string, startDate, endDate time.Time) bool { + // 这里假设数据中包含可解析的日期格式 + // 实际应根据数据格式进行调整 + // 例如: "2021-02-01 15:04:05 - Some data" + dateStr := strings.Split(data, " - ")[0] + dataDate, err := time.Parse("2006-01-02 15:04:05", dateStr) + if err != nil { + return true + } + + return (startDate.IsZero() || dataDate.After(startDate)) && (endDate.IsZero() || dataDate.Before(endDate)) +} + +// parseDateRange 解析日期范围 +func parseDateRange(dateRange []string) (startDate, endDate time.Time) { + if len(dateRange) >= 2 { + startDate, _ = time.Parse("2006-01-02", dateRange[0]) + endDate, _ = time.Parse("2006-01-02", dateRange[1]) + } + return +} + +// ListenForNewData 监听指定的 Redis key,对新数据执行处理函数,interval为轮询间隔 +func (r *RedisManager) ListenForNewData(ctx context.Context, key string, processor DataProcessor, interval time.Duration) { + var lastCheckedSize int64 = 0 + for { + select { + case <-ctx.Done(): + //fmt.Println("监听结束") + return + default: + // 获取当前 list 的大小 + currentSize, err := r.client.LLen(ctx, DeviceDataCachePrefix+key).Result() + if err != nil { + time.Sleep(time.Second) // 简单的错误恢复 + continue + } + + if currentSize > lastCheckedSize { + // 只检查自上次以来的新元素 + newElements, err := r.client.LRange(ctx, DeviceDataCachePrefix+key, 0, currentSize-lastCheckedSize-1).Result() + if err != nil { + fmt.Println("获取新元素失败:", err) + time.Sleep(time.Second) // 简单的错误恢复 + continue + } + + // 使用传入的处理函数处理新元素 + for _, element := range newElements { + processor(element) + } + + // 更新上次检查的位置 + lastCheckedSize = currentSize + } + + // 休眠以减少轮询频率 + time.Sleep(interval) + } + } +} diff --git a/pkg/general/db.go b/pkg/general/db.go new file mode 100644 index 0000000..1760fbd --- /dev/null +++ b/pkg/general/db.go @@ -0,0 +1,124 @@ +package general + +import ( + "context" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/os/gtime" +) + +// SelectReq 查询请求参数 +type SelectReq struct { + Param map[string]interface{} `p:"param"` //搜索哪些字段的数据 + KeyWords string `p:"keyWords"` //关键字搜索 + Year string `p:"year"` //提取哪一年的数据 + DateRange []string `p:"dateRange"` //提取指定日期范围的数据 + Accurate string `p:"accurate"` //数据精确类型(m:月,d:天,h:小时) + AccurateRanges string `p:"accurateRanges"` //当accurate精确类型为d的时候,可以指定月份,当accurate为h的时候,可以指定某日的值 + PageNum int `p:"pageNum"` //当前页码 + PageSize int `p:"pageSize"` //每页数 +} + +// DataListByPageRes 分页查询返回结果 +type DataListByPageRes struct { + Total int `json:"total"` //总数 + CurrentPage int `json:"currentPage"` //当前页码 + Data interface{} `json:"data"` //数据 +} + +// ListByPage 列表查询 +func ListByPage(ctx context.Context, dataModel *gdb.Model, req *SelectReq, vagueField []string) (res DataListByPageRes, err error) { + + fieldsStr := "*" + groupStr := "" + + if req != nil { + + if req.Param == nil { + req.Param = make(map[string]interface{}) + } + + if req.Accurate != "" { + switch req.Accurate { + case "y": //精度按月为单位筛选数据 + fieldsStr = "*,DATE_FORMAT(created_at,'%Y') time_num,created_at" + groupStr = "DATE_FORMAT(created_at,'%Y')" + case "m": //精度按月为单位筛选数据 + fieldsStr = "*,DATE_FORMAT(created_at,'%m') time_num,created_at" + groupStr = "DATE_FORMAT(created_at,'%Y-%m')" + case "d": //精度按天为单位筛选数据 + y := gtime.Now().Format("Y") + m := gtime.Now().Format("m") + if req.AccurateRanges == "" { + req.Param["DATE_FORMAT(created_at,'%Y-%m')"] = y + "-" + m + } else { + req.Param["DATE_FORMAT(created_at,'%Y-%m')"] = y + "-" + req.AccurateRanges + } + fieldsStr = "*,DATE_FORMAT(created_at,'%d') time_num,created_at" + groupStr = "DATE_FORMAT(created_at,'%Y-%m-%d')" + case "h": //精度按小时为单位筛选数据 + y := gtime.Now().Format("Y") + m := gtime.Now().Format("m") + d := gtime.Now().Format("d") + if req.AccurateRanges == "" { + req.Param["DATE_FORMAT(created_at,'%Y-%m-%d')"] = y + "-" + m + "-" + d + } else { + req.Param["DATE_FORMAT(created_at,'%Y-%m')"] = y + "-" + m + "-" + req.AccurateRanges + } + fieldsStr = "*,DATE_FORMAT(created_at,'%H') time_num,created_at" + groupStr = "DATE_FORMAT('%H',created_at)" + default: + + } + + } + + //KeyWords不为空的时候,进行模糊查询处理 + if req.KeyWords != "" && vagueField != nil { + for _, vf := range vagueField { + dataModel = dataModel.WhereOrLike(vf, "%"+req.KeyWords+"%") + + } + } + + if req.Year != "" { + req.Param["DATE_FORMAT(created_at,'%Y')"] = req.Year + } else { + if len(req.DateRange) > 0 { + dataModel = dataModel.WhereBetween("created_at", req.DateRange[0], req.DateRange[1]) + } + } + + dataModel = dataModel.Where(req.Param).Fields(fieldsStr).Group(groupStr) + + } else { + dataModel = dataModel.Fields(fieldsStr).Group(groupStr) + req = new(SelectReq) + } + + countModel := dataModel + totalData, err := countModel.All() + res.Total = totalData.Len() + if err != nil { + glog.Debug(ctx, err) + err = gerror.New("获取总行数失败") + return + } + if req.PageNum == 0 { + req.PageNum = 1 + } + res.CurrentPage = req.PageNum + req.PageSize = g.Cfg().MustGet(ctx, "system.DefaultPageSize").Int() + if req.PageSize == 0 { + req.PageSize = 1000 + } + + res.Data, err = dataModel.Page(res.CurrentPage, req.PageSize).All() + if err != nil { + err = gerror.New("获取数据失败") + return + } + return +} diff --git a/pkg/gftoken/LICENSE b/pkg/gftoken/LICENSE new file mode 100644 index 0000000..3b1b197 --- /dev/null +++ b/pkg/gftoken/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 tiger1103 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/pkg/gftoken/cache.go b/pkg/gftoken/cache.go new file mode 100644 index 0000000..cdd122e --- /dev/null +++ b/pkg/gftoken/cache.go @@ -0,0 +1,34 @@ +package gftoken + +import ( + "context" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/util/gconv" + "time" +) + +func (m *GfToken) contains(ctx context.Context, key string) bool { + ok, _ := m.cache.Contains(ctx, key) + return ok +} + +func (m *GfToken) setCache(ctx context.Context, key string, value interface{}) error { + return m.cache.Set(ctx, key, value, time.Duration(m.Timeout+m.MaxRefresh)*time.Second) +} + +func (m *GfToken) getCache(ctx context.Context, key string) (tData *TokenData, err error) { + var result *gvar.Var + result, err = m.cache.Get(ctx, key) + if err != nil { + return + } + if result.Val() != nil { + err = gconv.Struct(result, &tData) + } + return +} + +func (m *GfToken) removeCache(ctx context.Context, key string) (err error) { + _, err = m.cache.Remove(ctx, key) + return +} diff --git a/pkg/gftoken/claims.go b/pkg/gftoken/claims.go new file mode 100644 index 0000000..aba2197 --- /dev/null +++ b/pkg/gftoken/claims.go @@ -0,0 +1,21 @@ +package gftoken + +import "github.com/golang-jwt/jwt/v5" + +const ( + //token部分 + ErrorsParseTokenFail string = "解析token失败" + ErrorsTokenInvalid string = "无效的token" + ErrorsTokenNotActiveYet string = "Token 尚未激活" + ErrorsTokenMalFormed string = "Token 格式不正确" + + JwtTokenOK int = 200100 //token有效 + JwtTokenInvalid int = -400100 //无效的token + JwtTokenExpired int = -400101 //过期的token + JwtTokenFormatErrCode int = -400102 //提交的 Token 格式错误 +) + +type CustomClaims struct { + Data interface{} + jwt.RegisteredClaims +} diff --git a/pkg/gftoken/gftoken.go b/pkg/gftoken/gftoken.go new file mode 100644 index 0000000..37db856 --- /dev/null +++ b/pkg/gftoken/gftoken.go @@ -0,0 +1,246 @@ +package gftoken + +import ( + "context" + "errors" + "github.com/gogf/gf/v2/crypto/gaes" + "github.com/gogf/gf/v2/crypto/gmd5" + "github.com/gogf/gf/v2/encoding/gbase64" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gcache" + "github.com/gogf/gf/v2/text/gstr" + "github.com/gogf/gf/v2/util/grand" + "github.com/golang-jwt/jwt/v5" + "time" +) + +type GfToken struct { + // server name + ServerName string + // 缓存key (每创建一个实例CacheKey必须不相同) + CacheKey string + // 超时时间 默认10天(秒) + Timeout int64 + // 缓存刷新时间 默认5天(秒) + // 处理携带token的请求时当前时间大于超时时间并小于缓存刷新时间时token将自动刷新即重置token存活时间 + // MaxRefresh值为0时,token将不会自动刷新 + MaxRefresh int64 + // 是否允许多点登录 + MultiLogin bool + // Token加密key 32位 + EncryptKey []byte + // 缓存 (缓存模式:gcache 或 gredis) + cache *gcache.Cache + // 拦截排除地址 + ExcludePaths g.SliceStr + // jwt + userJwt *JwtSign +} + +// TokenData Token 数据 +type TokenData struct { + JwtToken string `json:"jwtToken"` + UuId string `json:"uuId"` +} + +// 存活时间 (存活时间 = 超时时间 + 缓存刷新时间) +func (m *GfToken) diedLine() time.Time { + return time.Now().Add(time.Second * time.Duration(m.Timeout+m.MaxRefresh)) +} + +// 生成token +func (m *GfToken) GenerateToken(ctx context.Context, key string, data interface{}) (keys string, err error) { + var ( + uuid string + tData *TokenData + tokens string + ) + // 支持多端重复登录,返回新token + if m.MultiLogin { + tData, err = m.getCache(ctx, m.CacheKey+key) + if err != nil { + return + } + if tData != nil { + key = gstr.SubStr(key, 0, len(key)-16) + grand.Letters(16) + keys, uuid, err = m.EncryptToken(ctx, key, tData.UuId) + m.doRefresh(ctx, key, tData) //刷新token + return + } + } + tokens, err = m.userJwt.CreateToken(CustomClaims{ + data, + jwt.RegisteredClaims{ + NotBefore: jwt.NewNumericDate(time.Unix(time.Now().Unix()-10, 0)), // 生效开始时间 + ExpiresAt: jwt.NewNumericDate(m.diedLine()), // 失效截止时间 + }, + }) + if err != nil { + return + } + keys, uuid, err = m.EncryptToken(ctx, key) + if err != nil { + return + } + err = m.setCache(ctx, m.CacheKey+key, TokenData{ + JwtToken: tokens, + UuId: uuid, + }) + if err != nil { + return + } + return +} + +// 解析token (只验证格式并不验证过期) +func (m *GfToken) ParseToken(r *ghttp.Request) (*CustomClaims, error) { + token, err := m.GetToken(r) + if err != nil { + return nil, err + } + if customClaims, err := m.userJwt.ParseToken(token.JwtToken); err == nil { + return customClaims, nil + } else { + return &CustomClaims{}, errors.New(ErrorsParseTokenFail) + } +} + +// 检查缓存的token是否有效且自动刷新缓存token +func (m *GfToken) IsEffective(ctx context.Context, token string) bool { + cacheToken, key, err := m.GetTokenData(ctx, token) + if err != nil { + g.Log().Info(ctx, err) + return false + } + _, code := m.IsNotExpired(cacheToken.JwtToken) + if JwtTokenOK == code { + // 刷新缓存 + if m.IsRefresh(cacheToken.JwtToken) { + return m.doRefresh(ctx, key, cacheToken) + } + return true + } + return false +} + +func (m *GfToken) doRefresh(ctx context.Context, key string, cacheToken *TokenData) bool { + if newToken, err := m.RefreshToken(cacheToken.JwtToken); err == nil { + cacheToken.JwtToken = newToken + err = m.setCache(ctx, m.CacheKey+key, cacheToken) + if err != nil { + g.Log().Error(ctx, err) + return false + } + } + return true +} + +func (m *GfToken) GetTokenData(ctx context.Context, token string) (tData *TokenData, key string, err error) { + var uuid string + key, uuid, err = m.DecryptToken(ctx, token) + if err != nil { + return + } + tData, err = m.getCache(ctx, m.CacheKey+key) + if tData == nil || tData.UuId != uuid { + err = gerror.New("token is invalid") + } + return +} + +// 检查token是否过期 (过期时间 = 超时时间 + 缓存刷新时间) +func (m *GfToken) IsNotExpired(token string) (*CustomClaims, int) { + if customClaims, err := m.userJwt.ParseToken(token); err == nil { + if time.Now().Unix()-customClaims.ExpiresAt.Unix() < 0 { + // token有效 + return customClaims, JwtTokenOK + } else { + // 过期的token + return customClaims, JwtTokenExpired + } + } else { + // 无效的token + return customClaims, JwtTokenInvalid + } +} + +// 刷新token的缓存有效期 +func (m *GfToken) RefreshToken(oldToken string) (newToken string, err error) { + if newToken, err = m.userJwt.RefreshToken(oldToken, m.diedLine().Unix()); err != nil { + return + } + return +} + +// token是否处于刷新期 +func (m *GfToken) IsRefresh(token string) bool { + if m.MaxRefresh == 0 { + return false + } + if customClaims, err := m.userJwt.ParseToken(token); err == nil { + now := time.Now().Unix() + if now < customClaims.ExpiresAt.Unix() && now > (customClaims.ExpiresAt.Unix()-m.MaxRefresh) { + return true + } + } + return false +} + +// EncryptToken token加密方法 +func (m *GfToken) EncryptToken(ctx context.Context, key string, randStr ...string) (encryptStr, uuid string, err error) { + if key == "" { + err = gerror.New("encrypt key empty") + return + } + // 生成随机串 + if len(randStr) > 0 { + uuid = randStr[0] + } else { + uuid = gmd5.MustEncrypt(grand.Letters(10)) + } + token, err := gaes.Encrypt([]byte(key+uuid), m.EncryptKey) + if err != nil { + g.Log().Error(ctx, "[GFToken]encrypt error Token:", key, err) + err = gerror.New("encrypt error") + return + } + encryptStr = gbase64.EncodeToString(token) + return +} + +// DecryptToken token解密方法 +func (m *GfToken) DecryptToken(ctx context.Context, token string) (DecryptStr, uuid string, err error) { + if token == "" { + err = gerror.New("decrypt Token empty") + return + } + token64, err := gbase64.Decode([]byte(token)) + if err != nil { + g.Log().Info(ctx, "[GFToken]decode error Token:", token, err) + err = gerror.New("decode error") + return + } + decryptToken, err := gaes.Decrypt(token64, m.EncryptKey) + if err != nil { + g.Log().Info(ctx, "[GFToken]decrypt error Token:", token, err) + err = gerror.New("decrypt error") + return + } + length := len(decryptToken) + uuid = string(decryptToken[length-32:]) + DecryptStr = string(decryptToken[:length-32]) + return +} + +// RemoveToken 删除token +func (m *GfToken) RemoveToken(ctx context.Context, token string) (err error) { + var key string + _, key, err = m.GetTokenData(ctx, token) + if err != nil { + return + } + err = m.removeCache(ctx, m.CacheKey+key) + return +} diff --git a/pkg/gftoken/jwt.go b/pkg/gftoken/jwt.go new file mode 100644 index 0000000..abfd71f --- /dev/null +++ b/pkg/gftoken/jwt.go @@ -0,0 +1,55 @@ +package gftoken + +import ( + "errors" + "github.com/golang-jwt/jwt/v5" + "time" +) + +// 使用工厂创建一个 JWT 结构体 +func CreateMyJWT(JwtTokenSignKey string) *JwtSign { + return &JwtSign{ + []byte(JwtTokenSignKey), + } +} + +// 定义一个 JWT验签 结构体 +type JwtSign struct { + SigningKey []byte +} + +// CreateToken 生成一个token +func (j *JwtSign) CreateToken(claims CustomClaims) (string, error) { + // 生成jwt格式的header、claims 部分 + tokenPartA := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + // 继续添加秘钥值,生成最后一部分 + return tokenPartA.SignedString(j.SigningKey) +} + +// 解析Token (只验证格式并不验证过期) +func (j *JwtSign) ParseToken(tokenString string) (*CustomClaims, error) { + token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { + return j.SigningKey, nil + }) + if err != nil { + return nil, err + } + if token == nil { + return nil, errors.New(ErrorsTokenInvalid) + } + if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { + return claims, nil + } else { + return nil, errors.New(ErrorsTokenInvalid) + } +} + +// 更新token有效期 +func (j *JwtSign) RefreshToken(tokenString string, extraAddSeconds int64) (string, error) { + if customClaims, err := j.ParseToken(tokenString); err == nil { + customClaims.ExpiresAt = jwt.NewNumericDate(time.Unix(extraAddSeconds, 0)) + return j.CreateToken(*customClaims) + } else { + return "", err + } +} diff --git a/pkg/gftoken/middleware.go b/pkg/gftoken/middleware.go new file mode 100644 index 0000000..bf9d9ef --- /dev/null +++ b/pkg/gftoken/middleware.go @@ -0,0 +1,53 @@ +package gftoken + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/text/gstr" + "strings" +) + +// Middleware 绑定group +func (m *GfToken) Middleware(group *ghttp.RouterGroup) error { + group.Middleware(m.authMiddleware) + return nil +} + +func (m *GfToken) authMiddleware(r *ghttp.Request) { + b, res := m.IsLogin(r) + if !b { + r.Response.WriteJson(res) + return + } + r.Middleware.Next() +} + +// AuthPath 判断路径是否需要进行认证拦截 +// return true 需要认证 +func (m *GfToken) AuthPath(urlPath string) bool { + // 去除后斜杠 + if strings.HasSuffix(urlPath, "/") { + urlPath = gstr.SubStr(urlPath, 0, len(urlPath)-1) + } + // 排除路径处理,到这里nextFlag为true + for _, excludePath := range m.ExcludePaths { + tmpPath := excludePath + // 前缀匹配 + if strings.HasSuffix(tmpPath, "/*") { + tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-2) + if gstr.HasPrefix(urlPath, tmpPath) { + // 前缀匹配不拦截 + return false + } + } else { + // 全路径匹配 + if strings.HasSuffix(tmpPath, "/") { + tmpPath = gstr.SubStr(tmpPath, 0, len(tmpPath)-1) + } + if urlPath == tmpPath { + // 全路径匹配不拦截 + return false + } + } + } + return true +} diff --git a/pkg/gftoken/options.go b/pkg/gftoken/options.go new file mode 100644 index 0000000..ccf2811 --- /dev/null +++ b/pkg/gftoken/options.go @@ -0,0 +1,114 @@ +package gftoken + +import ( + _ "github.com/gogf/gf/contrib/nosql/redis/v2" + "github.com/gogf/gf/v2/database/gredis" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gcache" +) + +var ( + defaultGFToken = GfToken{ + ServerName: "defaultGFToken", + CacheKey: "defaultGFToken_", + Timeout: 60 * 60 * 24 * 10, + MaxRefresh: 60 * 60 * 24 * 5, + cache: gcache.New(), + userJwt: CreateMyJWT("defaultGFToken"), + MultiLogin: false, + EncryptKey: []byte("49c54195e750b04e74a8429b17aefc77"), + } +) + +type OptionFunc func(*GfToken) + +func NewGfToken(opts ...OptionFunc) *GfToken { + gtoken := defaultGFToken + for _, o := range opts { + o(>oken) + } + return >oken +} + +func WithExcludePaths(value g.SliceStr) OptionFunc { + return func(g *GfToken) { + g.ExcludePaths = value + } +} + +func WithEncryptKey(value []byte) OptionFunc { + return func(g *GfToken) { + g.EncryptKey = value + } +} + +func WithServerName(value string) OptionFunc { + return func(g *GfToken) { + g.ServerName = value + } +} + +func WithCacheKey(value string) OptionFunc { + return func(g *GfToken) { + g.CacheKey = value + } +} + +func WithTimeoutAndMaxRefresh(timeout, maxRefresh int64) OptionFunc { + return func(g *GfToken) { + g.Timeout = timeout + g.MaxRefresh = maxRefresh + } +} + +func WithTimeout(value int64) OptionFunc { + return func(g *GfToken) { + g.Timeout = value + } +} + +func WithMaxRefresh(value int64) OptionFunc { + return func(g *GfToken) { + g.MaxRefresh = value + } +} + +func WithUserJwt(key string) OptionFunc { + return func(g *GfToken) { + g.userJwt = CreateMyJWT(key) + } +} + +func WithGCache() OptionFunc { + return func(g *GfToken) { + g.cache = gcache.New() + } +} + +func WithGRedis(redis ...*gredis.Redis) OptionFunc { + return func(gf *GfToken) { + gf.cache = gcache.New() + if len(redis) > 0 { + gf.cache.SetAdapter(gcache.NewAdapterRedis(redis[0])) + } else { + gf.cache.SetAdapter(gcache.NewAdapterRedis(g.Redis())) + } + } +} + +func WithGRedisConfig(redisConfig ...*gredis.Config) OptionFunc { + return func(g *GfToken) { + g.cache = gcache.New() + redis, err := gredis.New(redisConfig...) + if err != nil { + panic(err) + } + g.cache.SetAdapter(gcache.NewAdapterRedis(redis)) + } +} + +func WithMultiLogin(b bool) OptionFunc { + return func(g *GfToken) { + g.MultiLogin = b + } +} diff --git a/pkg/gftoken/utils.go b/pkg/gftoken/utils.go new file mode 100644 index 0000000..ec9cc56 --- /dev/null +++ b/pkg/gftoken/utils.go @@ -0,0 +1,57 @@ +package gftoken + +import ( + "github.com/gogf/gf/v2/net/ghttp" + "strings" +) + +const FailedAuthCode = 401 + +type AuthFailed struct { + Code int `json:"code"` + Message string `json:"message"` +} + +func (m *GfToken) GetRequestToken(r *ghttp.Request) (token string) { + authHeader := r.Header.Get("Authorization") + if authHeader != "" { + parts := strings.SplitN(authHeader, " ", 2) + if !(len(parts) == 2 && parts[0] == "Bearer") { + return + } else if parts[1] == "" { + return + } + token = parts[1] + } else { + authHeader = r.Get("token").String() + if authHeader == "" { + return + } + token = authHeader + } + return +} + +func (m *GfToken) GetToken(r *ghttp.Request) (tData *TokenData, err error) { + token := m.GetRequestToken(r) + tData, _, err = m.GetTokenData(r.GetCtx(), token) + return +} + +func (m *GfToken) IsLogin(r *ghttp.Request) (b bool, failed *AuthFailed) { + b = true + urlPath := r.URL.Path + if !m.AuthPath(urlPath) { + // 如果不需要认证,继续 + return + } + token := m.GetRequestToken(r) + if m.IsEffective(r.GetCtx(), token) == false { + b = false + failed = &AuthFailed{ + Code: FailedAuthCode, + Message: "token已失效", + } + } + return +} diff --git a/pkg/gpool/gpool.go b/pkg/gpool/gpool.go new file mode 100644 index 0000000..07f3e5b --- /dev/null +++ b/pkg/gpool/gpool.go @@ -0,0 +1,84 @@ +package gpool + +import ( + "context" + "sync" + "sync/atomic" + "time" +) + +type GPool struct { + sem chan struct{} + activeJobs *int32 // 使用原子操作追踪活跃的协程数目 + wg sync.WaitGroup + ctx context.Context + cancel context.CancelFunc + errChan chan error // 错误通道,用于收集任务执行中的错误 +} + +func NewGPool(capacity int) *GPool { + ctx, cancel := context.WithCancel(context.Background()) + return &GPool{ + sem: make(chan struct{}, capacity), + activeJobs: new(int32), + ctx: ctx, + cancel: cancel, + errChan: make(chan error, capacity), // 错误通道容量设置为工作池的容量 + } +} + +func (p *GPool) Acquire() bool { + select { + case p.sem <- struct{}{}: + atomic.AddInt32(p.activeJobs, 1) + p.wg.Add(1) + return true + case <-p.ctx.Done(): + return false // 如果上下文已取消,则不获取并返回false + } +} + +func (p *GPool) Release() { + <-p.sem + atomic.AddInt32(p.activeJobs, -1) + p.wg.Done() +} + +func (p *GPool) Go(job func(ctx context.Context) error) { + if p.Acquire() { + go func() { + defer p.Release() + if err := job(p.ctx); err != nil { + p.errChan <- err // 将错误发送到错误通道 + } + }() + } +} + +func (p *GPool) Wait(timeout time.Duration) bool { + done := make(chan struct{}) + go func() { + p.wg.Wait() + close(done) + }() + + select { + case <-done: + return true // 所有任务完成 + case <-time.After(timeout): + return false // 超时 + } +} + +func (p *GPool) Shutdown() { + p.cancel() // 发送取消信号 + if !p.Wait(5 * time.Second) { // 设置超时时间为5秒 + // 超时处理逻辑(可根据需要自定义) + } + close(p.errChan) // 关闭错误通道 +} + +// ErrChan 提供一个错误通道的访问方法 +func (p *GPool) ErrChan() <-chan error { + return p.errChan +} diff --git a/pkg/gpool/gpool_test.go b/pkg/gpool/gpool_test.go new file mode 100644 index 0000000..1daab51 --- /dev/null +++ b/pkg/gpool/gpool_test.go @@ -0,0 +1,49 @@ +package gpool + +import ( + "context" + "fmt" + "math/rand" + "testing" + "time" +) + +func TestGo(t *testing.T) { + // 初始化一个容量为5的工作池 + pool := NewGPool(5) + + // 模拟10个任务 + for i := 0; i < 10; i++ { + jobIndex := i + pool.Go(func(ctx context.Context) error { + // 随机等待时间,模拟任务执行 + waitTime := time.Duration(rand.Intn(1000)) * time.Millisecond + select { + case <-time.After(waitTime): + // 模拟任务有一定几率失败 + if rand.Float32() < 0.3 { + return fmt.Errorf("job %d failed", jobIndex) + } + fmt.Printf("job %d completed successfully\n", jobIndex) + return nil + case <-ctx.Done(): + // 上下文被取消,任务终止 + fmt.Printf("job %d cancelled\n", jobIndex) + return ctx.Err() + } + }) + } + + // 等待所有任务完成或超时 + if !pool.Wait(3 * time.Second) { + t.Log("Waited for 3 seconds, not all jobs completed") + } + + // 关闭工作池并处理错误 + pool.Shutdown() + + // 从错误通道处理和打印错误 + for err := range pool.ErrChan() { + t.Log("Received error:", err) + } +} diff --git a/pkg/iotModel/device.go b/pkg/iotModel/device.go new file mode 100644 index 0000000..537fa51 --- /dev/null +++ b/pkg/iotModel/device.go @@ -0,0 +1,152 @@ +package iotModel + +import ( + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/os/gtime" + "time" +) + +// 设备属性上报 +type ( + PropertyReportMessage struct { + Properties map[string]ReportPropertyNode `json:"properties"` //map类型,属性列表,key为属性标识,value为属性值 + } +) + +// 上报属性数据 +type ReportPropertyData map[string]ReportPropertyNode + +// 属性值 +type ReportPropertyNode struct { + Value any // 属性值 + CreateTime int64 // 上报时间 +} + +// 上报事件数据 +type ReportEventData struct { + Key string // 事件标识 + Param ReportEventParam // 事件输出参数 +} + +// 事件输出参数 +type ReportEventParam struct { + Value map[string]any // 事件输出参数 + CreateTime int64 // 上报时间 +} + +// 设备上下线状态 +type ReportStatusData struct { + Status string // 状态:online、offline + CreateTime int64 // 上下线时间 +} + +type DevicePropertiy struct { + Key string `json:"key" dc:"属性标识"` + Name string `json:"name" dc:"属性名称"` + Type string `json:"type" dc:"属性值类型"` + Unit string `json:"unit" dc:"属性值单位"` + Value *gvar.Var `json:"value" dc:"属性值"` + List []*gvar.Var `json:"list" dc:"当天属性值列表"` +} + +// DeviceLog 设备日志 +type DeviceLog struct { + Ts *gtime.Time `json:"ts" dc:"时间"` + Device string `json:"device" dc:"设备标识"` + Type string `json:"type" dc:"日志类型"` + Content string `json:"content" dc:"日志内容"` +} + +// 设备上线 +type DeviceOnlineMessage struct { + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + Desc string `json:"desc"` //string类型,描述信息 +} + +// 设备下线 +type DeviceOfflineMessage struct { + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + Desc string `json:"desc"` //string类型,描述信息 +} + +// 添加设备 +type DeviceAddMessage struct { + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + Desc string `json:"desc"` //string类型,描述信息 +} + +// 删除设备 +type DeviceDeleteMessage struct { + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + Desc string `json:"desc"` //string类型,描述信息 +} + +// 设备事件上报 +type ( + EventReportMessage struct { + EventId string `json:"eventId"` //string类型,事件标识 + Events map[string]interface{} `json:"events"` //map类型,事件列表,key为事件标识,value为事件值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 设备服务调用请求 +type ( + ServiceCallMessage struct { + ServiceId string `json:"serviceId"` //string类型,服务标识 + Params map[string]interface{} `json:"params"` //map类型,服务参数列表,key为参数标识,value为参数值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 平台接收到设备服务响应 +type ( + ServiceCallReplyMessage struct { + ServiceId string `json:"serviceId"` //string类型,服务标识 + Code int `json:"code"` //int类型,响应码 + Data map[string]interface{} `json:"data"` //map类型,响应数据列表,key为数据标识,value为数据值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 平台设置设备属性 +type ( + PropertySetMessage struct { + Properties map[string]interface{} `json:"properties"` //map类型,属性列表,key为属性标识,value为属性值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 平台接收到设置设备属性响应 +type ( + PropertySetReplyMessage struct { + Code int `json:"code"` //int类型,响应码 + Data map[string]interface{} `json:"data"` //map类型,响应数据列表,key为数据标识,value为数据值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 平台接收到配置设置响应 +type ( + ConfigSetReplyMessage struct { + Code int `json:"code"` //int类型,响应码 + Data map[string]interface{} `json:"data"` //map类型,响应数据列表,key为数据标识,value为数据值 + Timestamp int64 `json:"timestamp"` //int64类型,时间戳,单位为毫秒 + } +) + +// 平台接收到配置获取请求 + +type ( + ConfigGetMessage struct { + ConfigScope string `json:"configScope"` + GetType string `json:"getType"` + } +) + +// DeviceStatusLog 设备状态动态变化日志 +type DeviceStatusLog struct { + DeviceKey string + Status int //设备状态 + Timestamp time.Time //数据时间 +} diff --git a/pkg/iotModel/sagooProtocol/common.go b/pkg/iotModel/sagooProtocol/common.go new file mode 100644 index 0000000..994ff2d --- /dev/null +++ b/pkg/iotModel/sagooProtocol/common.go @@ -0,0 +1,17 @@ +package sagooProtocol + +type ( + // ack 标记是否需要回复,1需要回复,0不需要回复 + SysInfo struct { + Ack int `json:"ack"` + } + // 属性节点,包含属性值和时间 + PropertyNode struct { + Value interface{} `json:"value"` + CreateTime int64 `json:"time"` + } +) + +const ( + NeedAck = 1 +) diff --git a/pkg/iotModel/sagooProtocol/config.go b/pkg/iotModel/sagooProtocol/config.go new file mode 100644 index 0000000..49564ff --- /dev/null +++ b/pkg/iotModel/sagooProtocol/config.go @@ -0,0 +1,57 @@ +package sagooProtocol + +type ( + // 配置下发报文 + ConfigPushRequest struct { + Id string `json:"id"` + Version string `json:"version"` + Params ConfigPushRequestData `json:"params"` + Method string `json:"method"` + } + ConfigPushRequestData struct { + ConfigId string `json:"configId"` + ConfigSize int `json:"configSize"` + ConfigContent string `json:"configContent"` + Sign string `json:"sign"` + SignMethod string `json:"signMethod"` + Url string `json:"url"` + GetType string `json:"getType"` + } + // 配置下发报文响应 + ConfigPushResponse struct { + Code int `json:"code"` + Data map[string]interface{} `json:"data"` + Id string `json:"id"` + } +) + +type ( + // 配置获取 + ConfigGetRequest struct { + Id string `json:"id"` + Version string `json:"version"` + Sys struct { + Ack int `json:"ack"` + } `json:"sys"` + Params struct { + ConfigScope string `json:"configScope"` + GetType string `json:"getType"` + } `json:"params"` + Method string `json:"method"` + } + ConfigGetResponse struct { + Id string `json:"id"` + Version string `json:"version"` + Code int `json:"code"` + Data ConfigGetResponseData `json:"data"` + } + ConfigGetResponseData struct { + ConfigId string `json:"configId"` + ConfigSize int `json:"configSize"` + Sign string `json:"sign"` + SignMethod string `json:"signMethod"` + Url string `json:"url"` + GetType string `json:"getType"` + ConfigContent string `json:"configContent"` + } +) diff --git a/pkg/iotModel/sagooProtocol/event.go b/pkg/iotModel/sagooProtocol/event.go new file mode 100644 index 0000000..72d1b5e --- /dev/null +++ b/pkg/iotModel/sagooProtocol/event.go @@ -0,0 +1,29 @@ +package sagooProtocol + +// 相关topci信息 +const () + +// 事件上报结构体 +type ( + // 事件上报请求报文 + ReportEventReq struct { + Id string `json:"id"` + Version string `json:"version"` + Sys SysInfo `json:"sys"` + Params ReportEventParams `json:"params"` + } + ReportEventParams struct { + Value map[string]string `json:"value"` + CreateAt int64 `json:"time"` + } + // 事件上报响应报文 + ReportEventReply struct { + Code int `json:"code"` + Data struct { + } `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Method string `json:"method"` + Version string `json:"version"` + } +) diff --git a/pkg/iotModel/sagooProtocol/gateway.go b/pkg/iotModel/sagooProtocol/gateway.go new file mode 100644 index 0000000..34c8653 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/gateway.go @@ -0,0 +1,41 @@ +package sagooProtocol + +// 网关批量上报结构体 +type ( + // 网关批量上报请求报文 + GatewayBatchReq struct { + Id string `json:"id"` + Version string `json:"version"` + Sys SysInfo `json:"sys"` + Params PropertyInfo `json:"params"` + Method string `json:"method"` + } + PropertyInfo struct { + Properties map[string]interface{} `json:"properties"` + Events map[string]EventNode `json:"events"` + SubDevices []Sub `json:"subDevices"` + } + Sub struct { + Identity Identity `json:"identity"` + Properties map[string]interface{} `json:"properties"` + Events map[string]EventNode `json:"events"` + } + Identity struct { + ProductKey string `json:"productKey"` + DeviceKey string `json:"deviceKey"` + } + EventNode struct { + Value map[string]interface{} `json:"value"` + CreateTime int64 `json:"time"` + } + // 网关批量上报响应报文 + GatewayBatchReply struct { + Code int `json:"code"` + Data struct { + } `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Method string `json:"method"` + Version string `json:"version"` + } +) diff --git a/pkg/iotModel/sagooProtocol/inform.go b/pkg/iotModel/sagooProtocol/inform.go new file mode 100644 index 0000000..fb0d591 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/inform.go @@ -0,0 +1,29 @@ +package sagooProtocol + +type InformRequest struct { + Id string `json:"id"` + Params struct { + Version string `json:"version"` + Module string `json:"module"` + } `json:"params"` +} + +type ( + UpgradeCommandRequest struct { + Code string `json:"code"` + Data UpgradeCommandRequestData `json:"data"` + Id int `json:"id"` + Message string `json:"message"` + } + + UpgradeCommandRequestData struct { + Size int `json:"size"` + Version string `json:"version"` + SignMethod string `json:"signMethod"` + DProtocol string `json:"dProtocol"` + Url string `json:"url"` + Sign string `json:"sign"` + Module string `json:"module"` + ExtData map[string]string `json:"extData"` + } +) diff --git a/pkg/iotModel/sagooProtocol/north/base.go b/pkg/iotModel/sagooProtocol/north/base.go new file mode 100644 index 0000000..5bb673a --- /dev/null +++ b/pkg/iotModel/sagooProtocol/north/base.go @@ -0,0 +1,29 @@ +package north + +import ( + "context" +) + +// Message 北向消息通用结构体 +type Message struct { + Meta map[string]string `json:"meta"` //消息元数据,里面的字段暂时为空 + MessageId string `json:"messageId"` // 消息id,`string`类型,消息唯一标识 + ProductKey string `json:"productKey"` // 产品`key`,`string`类型 + DeviceKey string `json:"deviceKey"` //设备`key`,`string`类型 + Data interface{} `json:"data"` // 消息体,里面的字段根据不同的消息类型会有不同的结构体 +} + +// WriteMessage 北向消息发送 +func WriteMessage(ctx context.Context, topic string, meta map[string]string, productKey, deviceKey string, data interface{}) { + //m := Message{ + // Meta: meta, + // MessageId: guid.S(), + // ProductKey: productKey, + // DeviceKey: deviceKey, + // Data: data, + //} + //payload, _ := json.Marshal(m) + //if err := mqtt.Publish(topic, payload); err != nil { + // g.Log().Errorf(ctx, "publish north-message error: %s, topic:%s, message:%s", err, topic, string(payload)) + //} +} diff --git a/pkg/iotModel/sagooProtocol/north/topic.go b/pkg/iotModel/sagooProtocol/north/topic.go new file mode 100644 index 0000000..88e2eb4 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/north/topic.go @@ -0,0 +1,30 @@ +package north + +const ( + // 设备上线 + DeviceOnlineMessageTopic = "/message/device/online" + // 设备下线 + DeviceOfflineMessageTopic = "/message/device/offline" + // 设备添加 + DeviceAddMessageTopic = "/message/device/add" + // 设备删除 + DeviceDeleteMessageTopic = "/message/device/delete" + // 设备上报属性 + PropertyReportMessageTopic = "/message/tsl/receive/property/report" + // 设备上报事件 + EventReportMessageTopic = "/message/tsl/receive/event/report" + // 平台调用设备服务请求 + ServiceCallMessageTopic = "/message/tsl/send/service/call" + // 平台接收到设备服务响应 + ServiceReplyMessageTopic = "/message/tsl/receive/service/reply" + // 平台设置设备属性 + PropertySetMessageTopic = "/message/tsl/send/property/set" + // 平台接收到设置设备属性响应 + PropertySetReplyMessageTopic = "/message/tsl/receive/property/reply" + // 平台下发配置 + ConfigSendMessageTopic = "/message/tsl/send/config" + // 平台下发配置响应 + ConfigSendMessageReplyTopic = "/message/tsl/receive/config/reply" + // 平台获取配置 + ConfigGetMessageTopic = "/message/tsl/receive/config/get" +) diff --git a/pkg/iotModel/sagooProtocol/process.go b/pkg/iotModel/sagooProtocol/process.go new file mode 100644 index 0000000..5d26541 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/process.go @@ -0,0 +1,14 @@ +package sagooProtocol + +type ( + ProProgress struct { + Id string `json:"id"` + Params ProProgressParams `json:"params"` + } + + ProProgressParams struct { + Step string `json:"step"` + Desc string `json:"desc"` + Module string `json:"module"` + } +) diff --git a/pkg/iotModel/sagooProtocol/property.go b/pkg/iotModel/sagooProtocol/property.go new file mode 100644 index 0000000..b654b51 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/property.go @@ -0,0 +1,41 @@ +package sagooProtocol + +// 属性上报结构体 +type ( + // 属性上报请求报文 + ReportPropertyReq struct { + Id string `json:"id"` + Version string `json:"version"` + Sys SysInfo `json:"sys"` + Params map[string]interface{} `json:"params"` + Method string `json:"method"` + } + // 属性上报响应报文 + ReportPropertyReply struct { + Code int `json:"code"` + Data struct { + } `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Method string `json:"method"` + Version string `json:"version"` + } +) + +type ( + // 属性下发平台请求报文 + PropertySetRequest struct { + Id string `json:"id"` + Version string `json:"version"` + Params map[string]interface{} `json:"params"` + Method string `json:"method"` + } + // 属性下发设备响应报文 + PropertySetRes struct { + Code int `json:"code"` + Data map[string]interface{} `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Version string `json:"version"` + } +) diff --git a/pkg/iotModel/sagooProtocol/service.go b/pkg/iotModel/sagooProtocol/service.go new file mode 100644 index 0000000..9d505ac --- /dev/null +++ b/pkg/iotModel/sagooProtocol/service.go @@ -0,0 +1,20 @@ +package sagooProtocol + +// 服务调用结构体 +type ( + //服务下发请求报文 + ServiceCallRequest struct { + Id string `json:"id"` + Version string `json:"version"` + Params map[string]interface{} `json:"params"` + Method string `json:"method"` + } + //服务下发响应报文 + ServiceCallOutputRes struct { + Code int `json:"code"` + Data map[string]interface{} `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Version string `json:"version"` + } +) diff --git a/pkg/iotModel/sagooProtocol/topic.go b/pkg/iotModel/sagooProtocol/topic.go new file mode 100644 index 0000000..f9fd859 --- /dev/null +++ b/pkg/iotModel/sagooProtocol/topic.go @@ -0,0 +1,66 @@ +package sagooProtocol + +// 平台下发topic +const ( + //平台服务调用请求topic /sys/${productKey}/${devicekey}/thing/service/${tsl.service.identifier} + ServiceCallRegisterSubRequestTopic = "/sys/+/+/thing/service/+" + //平台服务调用响应求topic /sys/${productKey}/${devicekey}/thing/service/${tsl.service.identifier}_reply + //这里的请求和响应是不相同的,响应是最后的后缀reply结尾,需要注意 + ServiceCallRegisterSubResponseTopic = "/sys/+/+/thing/service/+" + + //平台设置属性请求topic /sys/${productKey}/${deviceKey}/thing/service/property/set + PropertySetRegisterSubRequestTopic = "/sys/+/+/thing/service/property/set" + //平台设置属性响应topic(设备端响应) /sys/${productKey}/thing/service/property/set_reply + PropertySetRegisterSubResponseTopic = "/sys/+/+/thing/service/property/set_reply" + + //平台下发配置请求topic /sys/${productKey}/${deviceKey}/thing/config/push + ConfigPushRegisterSubRequestTopic = "/sys/+/+/thing/config/push" + //平台下发配置响应topic /sys/${productKey}/${deviceKey}/thing/config/push/reply + ConfigPushRegisterSubResponseTopic = "/sys/+/+/thing/config/push/reply" +) + +const ( + //设备上报属性请求topic /sys/${productKey}/${deviceKey}/thing/event/property/post + PropertyRegisterSubRequestTopic = "/sys/+/+/thing/event/property/post" + //设备上报属性响应topic(平台响应) /sys/${productKey}/${deviceKey}/thing/event/property/post_reply + PropertyRegisterPubResponseTopic = "/sys/+/+/thing/event/property/post_reply" + + //设备上报事件请求topic /sys/${productKey}/${deviceKey}/thing/event/${tsl.event.identifier}/post + EventRegisterSubRequestTopic = "/sys/+/+/thing/event/+/post" + //设备上报事件响应topic(平台响应) /sys/${productKey}/${deviceKey}/thing/event/${tsl.event.identifier}_reply + EventRegisterPubResponseTopic = "/sys/+/+/thing/event/+/post_reply" + + // 设备上报批量属性请求topic /sys/${productKey}/${deviceKey}/thing/event/property/pack/post + BatchRegisterSubRequestTopic = "/sys/+/+/thing/event/property/pack/post" + // 设备上报批量属性响应topic(平台响应) /sys/${productKey}/${deviceKey}/thing/event/property/pack/post_reply + BatchRegisterPubResponseTopic = "/sys/+/+/thing/event/property/pack/post_reply" + + //设备主动请求配置信息(设备端发起) /sys/${productKey}/${deviceKey}/thing/config/get + ConfigGetRequestTopic = "/sys/+/+/thing/config/get" + //设备主动请求配置信息(平台响应) /sys/${productKey}/${deviceKey}/thing/config/get_reply + ConfigGetResponseTopic = "/sys/+/+/thing/config/get_reply" +) + +//ota相关 + +// 平台下发 +const ( + //推送ota升级包(平台侧发起)/ota/device/upgrade/${productKey}/${deviceKey} + OtaUpgradeCommandTopic = "/ota/device/upgrade/+/+" +) + +// 设备上报 +const ( + //设备上报版本信息topic(设备端发起) /ota/device/inform/${productKey}/${deviceKey} + OtaDeviceInformVersionInfoTopic = "/ota/device/inform/+/+" + //上报ota升级进度(设备端发起) /ota/device/progress/${productKey}/${deviceKey} + OtaUpgradeProgressTopic = "/ota/device/progress/+/+" + + //设备请求OTA升级包信息(设备端发起) /sys/${productKey}/${deviceKey}/thing/ota/firmware/get + OtaDeviceRequestUpgradePackageRequestTopic = "/sys/+/+/thing/ota/firmware/get" + OtaDeviceRequestUpgradePackageResponseTopic = "/sys/+/+/thing/ota/firmware/get_reply" + + //设备请求下载文件分片(设备端发起) /sys/${productKey}/${deviceKey}/thing/file/download + OtaDeviceRequestDownloadFileRequestTopic = "/sys/+/+/thing/file/download" + OtaDeviceRequestDownloadFileResponseTopic = "/sys/+/+/thing/file/download_reply" +) diff --git a/pkg/iotModel/topicModel/topicModel.go b/pkg/iotModel/topicModel/topicModel.go new file mode 100644 index 0000000..78440b3 --- /dev/null +++ b/pkg/iotModel/topicModel/topicModel.go @@ -0,0 +1,16 @@ +package topicModel + +import "sagooiot/internal/model" + +type TopicHandlerData struct { + Topic string + ProductKey string + DeviceKey string + PayLoad []byte + DeviceDetail *model.DeviceOutput +} + +type TopicDownHandlerData struct { + PayLoad []byte + DeviceDetail *model.DeviceOutput +} diff --git a/pkg/iotModel/tsk.go b/pkg/iotModel/tsk.go new file mode 100644 index 0000000..1e59356 --- /dev/null +++ b/pkg/iotModel/tsk.go @@ -0,0 +1,22 @@ +package iotModel + +import "github.com/gogf/gf/v2/os/gtime" + +type TsdTables struct { + TableName string `json:"tableName" description:"表名"` + DbName string `json:"dbName" description:"数据库名"` + StableName string `json:"stableName" description:"超级表名"` + CreateTime *gtime.Time `json:"createTime" description:"创建时间"` +} + +type TsdTableInfo struct { + Field string `json:"field" description:"字段名"` + Type string `json:"type" description:"类型"` + Length int `json:"length" description:"长度"` + Note string `json:"note" description:"note"` +} + +type TsdTableDataInfo struct { + Filed []string `json:"filed" description:"字段"` + Info []map[string]interface{} `json:"info" description:"数据"` +} diff --git a/pkg/jsinterpreter/js-interpreter.go b/pkg/jsinterpreter/js-interpreter.go new file mode 100644 index 0000000..90d2e6d --- /dev/null +++ b/pkg/jsinterpreter/js-interpreter.go @@ -0,0 +1,24 @@ +package jsinterpreter + +import ( + "fmt" + "github.com/robertkrimen/otto" + "strings" +) + +// RunScript 运行js脚本 +func RunScript(jsonStr string, jsCode string) (string, error) { + vm := otto.New() + if _, err := vm.Run(jsCode); err != nil { + return "", fmt.Errorf("failed to run JavaScript code: %v", err) + } + value, err := vm.Call("parse", nil, jsonStr) + if err != nil { + return "", fmt.Errorf("failed to call: %v", err) + } + data, err := value.MarshalJSON() + if err != nil { + return "", fmt.Errorf("failed to MarshalJSON: %v", err) + } + return strings.TrimPrefix(strings.TrimSuffix(string(data), "\""), "\""), nil +} diff --git a/pkg/mqttclient/mqtt.go b/pkg/mqttclient/mqtt.go new file mode 100644 index 0000000..d4d07ff --- /dev/null +++ b/pkg/mqttclient/mqtt.go @@ -0,0 +1,86 @@ +package mqttclient + +import ( + "context" + mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/gogf/gf/v2/frame/g" + "strconv" + "sync" + "time" +) + +var systemMqttClient *MqttWrapperClient + +type MqttConf struct { + Addr string + ClientId string + UserName string + Password string +} + +type MqttWrapperClient struct { + sync.RWMutex + subTopic map[string]mqtt.MessageHandler + c mqtt.Client +} + +func InitMqtt(c *MqttConf) (err error) { + opts := mqtt.NewClientOptions() + opts.AddBroker(c.Addr) + opts.SetClientID(c.ClientId + strconv.Itoa(time.Now().Nanosecond())) + opts.SetAutoReconnect(true) + opts.SetConnectRetryInterval(time.Second * 30) + opts.SetUsername(c.UserName) + opts.SetPassword(c.Password) + opts.SetConnectRetry(true) + opts.SetOnConnectHandler(func(client mqtt.Client) { + if systemMqttClient != nil { + for topic, handler := range systemMqttClient.subTopic { + if subErr := systemMqttClient.c.Subscribe(topic, 2, handler); subErr.Error() != nil { + g.Log().Errorf(context.TODO(), "reconnect subscribe error: %s", subErr.Error()) + } + } + } + }) + opts.SetConnectionLostHandler(func(client mqtt.Client, err error) { + g.Log().Debugf(context.TODO(), "MQTT连接断开... %s", err.Error()) + }) + err = getNewClient(opts) + return +} + +// getNewClient 获取新的mqtt客户端 +func getNewClient(opts *mqtt.ClientOptions) (err error) { + mqttClient := mqtt.NewClient(opts) + if token := mqttClient.Connect(); token.Wait() && token.Error() != nil { + return token.Error() + } else { + systemMqttClient = &MqttWrapperClient{ + c: mqttClient, + subTopic: make(map[string]mqtt.MessageHandler), + } + return nil + } +} + +func Close() { + systemMqttClient.c.Disconnect(uint(250)) +} + +func Publish(topic string, payload []byte) error { + pubToken := systemMqttClient.c.Publish(topic, 2, false, payload) + return pubToken.Error() +} + +func Subscribe(ctx context.Context, topic string, h mqtt.MessageHandler) (err error) { + qos := g.Cfg().MustGet(ctx, "mqtt.qos").Int() + if subErr := systemMqttClient.c.Subscribe(topic, byte(qos), h); subErr.Error() != nil { + g.Log().Errorf(ctx, "subscribe error: %s", subErr.Error()) + return subErr.Error() + } else { + systemMqttClient.Lock() + systemMqttClient.subTopic[topic] = h + systemMqttClient.Unlock() + return nil + } +} diff --git a/extend/consts/PluginHandleType/network.go b/pkg/plugins/consts/PluginHandleType/network.go similarity index 100% rename from extend/consts/PluginHandleType/network.go rename to pkg/plugins/consts/PluginHandleType/network.go diff --git a/extend/consts/PluginType/plugintype.go b/pkg/plugins/consts/PluginType/plugintype.go similarity index 100% rename from extend/consts/PluginType/plugintype.go rename to pkg/plugins/consts/PluginType/plugintype.go diff --git a/pkg/plugins/extend.go b/pkg/plugins/extend.go new file mode 100644 index 0000000..9452eaa --- /dev/null +++ b/pkg/plugins/extend.go @@ -0,0 +1,56 @@ +package plugins + +import ( + "context" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/hashicorp/go-plugin" + "sagooiot/pkg/plugins/consts/PluginType" + "sagooiot/pkg/plugins/module" +) + +var pluginsFilePath = "../plugins/built" + +// NewManager 创建插件管理器 +func NewManager(ptype, glob, dir string, pluginImpl plugin.Plugin) *Manager { + manager := &Manager{ + Type: ptype, + Glob: glob, + Path: dir, + Plugins: map[string]*PluginInfo{}, + pluginImpl: pluginImpl, + } + return manager +} + +// pluginInit 初始化插件管理器 +func pluginInit(sysPluginType string) (pm *Manager, err error) { + // 静态目录设置 + pluginsPath := g.Cfg().MustGet(context.TODO(), "system.pluginsPath").String() + if pluginsPath != "" { + pluginsFilePath = pluginsPath + } + + switch sysPluginType { + case PluginType.Notice: + pm = NewManager(sysPluginType, PluginType.Notice+"-*", pluginsFilePath, &module.NoticePlugin{}) + defer pm.Dispose() + + break + case PluginType.Protocol: + pm = NewManager(sysPluginType, PluginType.Protocol+"-*", pluginsFilePath, &module.ProtocolPlugin{}) + defer pm.Dispose() + + break + default: + err = gerror.New("无效的插件类型") + return + } + + //初始化管理器 + err = pm.Init() + //启动所有插件 + err = pm.Launch() + + return +} diff --git a/pkg/plugins/manager.go b/pkg/plugins/manager.go new file mode 100644 index 0000000..788a3a4 --- /dev/null +++ b/pkg/plugins/manager.go @@ -0,0 +1,219 @@ +package plugins + +import ( + "context" + "errors" + "fmt" + "github.com/gogf/gf/v2/encoding/gyaml" + "github.com/gogf/gf/v2/frame/g" + "github.com/hashicorp/go-plugin" + "os/exec" + "path/filepath" + "sagooiot/internal/consts" + "sagooiot/pkg/cache" + "sagooiot/pkg/plugins/consts/PluginType" + "sagooiot/pkg/plugins/module" + "strings" + "sync" +) + +// PluginInfo 插件信息 +type PluginInfo struct { + ID string + Path string + Stats bool + Client *plugin.Client +} + +// Manager 插件管理器, 为不同类型的插件,管理的生命周期 +type Manager struct { + Type string // 管理器处理的插件类型的id, 如protocol, notice + Glob string // 全局的插件文件名 + Path string // 插件路径 + Plugins map[string]*PluginInfo // 插件信息列表 + initialized bool // 是否初始化 + pluginImpl plugin.Plugin // 插件实现虚拟接口 +} + +// Init 初始化插件管理器 +func (m *Manager) Init() error { + + //发现插件绝对路径 + plugins, err := plugin.Discover(m.Glob, m.Path) + if err != nil { + return err + } + + //获取所有插件信息 + for _, p := range plugins { + _, file := filepath.Split(p) + globAster := strings.LastIndex(m.Glob, "*") + trim := m.Glob[0:globAster] + id := strings.TrimPrefix(file, trim) + + //添加到插件信息 + m.Plugins[id] = &PluginInfo{ + ID: id, + Path: p, + } + } + + m.initialized = true + + return nil +} + +// Launch 启动所有插件 +func (m *Manager) Launch() error { + for id, info := range m.Plugins { + g.Log().Debugf(context.Background(), "注册插件 type=%s, id=%s, impl=%s", m.Type, id, info.Path) + // 创建新的客户端 + // 以exec.Command方式启动插件进程,并创建宿主机进程和插件进程的连接 + client := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: module.HandshakeConfig, + Plugins: m.pluginMap(id), + Cmd: exec.Command(info.Path), + }) + + if _, ok := m.Plugins[id]; !ok { + // 如果没有找到,忽略? + continue + } + m.Plugins[id].Client = client + + } + + return nil +} + +// Dispose 释放插件资源 +func (m *Manager) Dispose() { + var wg sync.WaitGroup + for _, p := range m.Plugins { + wg.Add(1) + go func(client *plugin.Client) { + // 关闭client,释放相关资源,终止插件子程序的运行 + client.Kill() + wg.Done() + }(p.Client) + } + wg.Wait() +} + +// GetInterface 获取插件接口 +func (m *Manager) GetInterface(id string) (interface{}, error) { + + if _, ok := m.Plugins[id]; !ok { + return nil, fmt.Errorf("在注册的插件中找不到插件ID:%s", id) + } + //获取注册插件客户端 plugin.Client + client := m.Plugins[id].Client + + // 返回协议客户端,如rpc客户端或grpc客户端,用于后续通信 + rpcClient, err := client.Client() + if err != nil { + return nil, err + } + + // 根据指定插件名称分配新实例 + raw, err := rpcClient.Dispense(id) + if err != nil { + return nil, err + } + + return raw, nil +} + +// pluginMap //插件名称到插件对象的映射关系 +func (m *Manager) pluginMap(id string) map[string]plugin.Plugin { + pMap := map[string]plugin.Plugin{} + pMap[id] = m.pluginImpl + return pMap +} + +// GetPluginsConfig 获取插件配置 +func getPluginsConfigData(pluginType, pluginName string) (res map[interface{}]interface{}, err error) { + key := fmt.Sprintf(consts.PluginsTypeName, pluginType, pluginName) + pcgData, err := cache.Instance().Get(context.Background(), key) + if err != nil { + return + } + if pcgData == nil { + return nil, errors.New("插件缓存配置不存在") + } + err = gyaml.DecodeTo([]byte(pcgData.String()), &res) + return +} + +// StartPlugin 启用一个插件 +func (m *Manager) StartPlugin(id string) (err error) { + p := m.Plugins[id] + if p != nil { + p.Client.Kill() + } + path := g.Cfg().MustGet(context.Background(), "system.pluginsPath").String() + "/protocol-" + id + //path := "./plugins/built/protocol-" + id + client := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: module.HandshakeConfig, + Plugins: m.pluginMap(id), + Cmd: exec.Command(path), + }) + if err != nil { + g.Log().Error(context.Background(), id, "插件加载出错", err.Error()) + } + p.Client = client + g.Log().Debugf(context.Background(), "注册插件 type=%s, id=%s, impl=%s", m.Type, id, p.Path) + + return +} + +// StopPlugin 停用一个插件 +func (m *Manager) StopPlugin(id string) (err error) { + p := m.Plugins[id] + if p != nil { + p.Client.Kill() + } + return +} + +// AddPlugin 添加一个插件 +func (m *Manager) AddPlugin(Type string, id string) (err error) { + p := m.Plugins[id] + if p != nil { + err = errors.New("插件已存在") + return + } + path := "" + switch Type { + case PluginType.Protocol: + path = pluginsFilePath + "/protocol-" + id + break + case PluginType.Notice: + path = pluginsFilePath + "/notice-" + id + break + default: + err = errors.New("无效的插件类型") + } + + //添加到插件列 + m.Plugins[id] = &PluginInfo{ + ID: id, + Path: path, + } + + return +} + +// RemovePlugin 移除一个插件 +func (m *Manager) RemovePlugin(id string) (err error) { + p := m.Plugins[id] + if p == nil { + err = errors.New("插件不存在") + return + } + // 关闭client,释放相关资源,终止插件子程序的运行 + p.Client.Kill() + // 从插件列表中移除 + delete(m.Plugins, id) + return +} diff --git a/extend/model/base.go b/pkg/plugins/model/base.go similarity index 100% rename from extend/model/base.go rename to pkg/plugins/model/base.go diff --git a/extend/model/info.go b/pkg/plugins/model/info.go similarity index 100% rename from extend/model/info.go rename to pkg/plugins/model/info.go diff --git a/pkg/plugins/model/notice.go b/pkg/plugins/model/notice.go new file mode 100644 index 0000000..6aa710c --- /dev/null +++ b/pkg/plugins/model/notice.go @@ -0,0 +1,27 @@ +package model + +type NoticeSendObject struct { + Name string `json:"name"` + Value string `json:"value"` +} + +type NoticeInfoData struct { + ConfigId string `orm:"config_id" json:"config_id"` // + ComeFrom string `orm:"come_from" json:"come_from"` // + Method string `orm:"method" json:"method"` // + MethodCron string `orm:"method_cron" json:"method_cron"` // + MethodNum int `orm:"method_num" json:"method_num"` // + MsgTitle string `orm:"msg_title" json:"msg_title"` // + MsgBody string `orm:"msg_body" json:"msg_body"` // + MsgUrl string `orm:"msg_url" json:"msg_url"` // + UserIds string `orm:"user_ids" json:"user_ids"` // + PartyIds string `orm:"party_ids" json:"party_ids"` // + Totag []NoticeSendObject `orm:"totag" json:"totag"` // + TemplateCode string `orm:"template_code" json:"template_code"` // +} + +type NoticeData struct { + Config map[interface{}]interface{} + SendParam map[string]interface{} + Msg NoticeInfoData +} diff --git a/extend/module/module.go b/pkg/plugins/module/module.go similarity index 71% rename from extend/module/module.go rename to pkg/plugins/module/module.go index db396e2..cf78781 100644 --- a/extend/module/module.go +++ b/pkg/plugins/module/module.go @@ -3,17 +3,17 @@ package module import ( "encoding/json" "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/model" + "sagooiot/pkg/plugins/model" ) -//HandshakeConfig 握手配置,插件进程和宿主机进程,都需要保持一致 +// HandshakeConfig 握手配置,插件进程和宿主机进程,都需要保持一致 var HandshakeConfig = plugin.HandshakeConfig{ ProtocolVersion: 1, MagicCookieKey: "SAGOO_PLUGIN", MagicCookieValue: "sagoo_plugin", } -//OutJsonRes 输出json字符串结果 +// OutJsonRes 输出json字符串结果 func OutJsonRes(code int, message string, data interface{}) string { var res = model.JsonRes{} res.Code = code diff --git a/extend/module/notice.go b/pkg/plugins/module/notice.go similarity index 96% rename from extend/module/notice.go rename to pkg/plugins/module/notice.go index 53827b9..335213a 100644 --- a/extend/module/notice.go +++ b/pkg/plugins/module/notice.go @@ -2,8 +2,8 @@ package module import ( gplugin "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/model" "net/rpc" + "sagooiot/pkg/plugins/model" ) // Notice 通知服务插件接口 diff --git a/extend/module/protocol.go b/pkg/plugins/module/protocol.go similarity index 87% rename from extend/module/protocol.go rename to pkg/plugins/module/protocol.go index 3a8b946..79bdb26 100644 --- a/extend/module/protocol.go +++ b/pkg/plugins/module/protocol.go @@ -1,11 +1,13 @@ package module import ( + "context" "fmt" + "github.com/gogf/gf/v2/frame/g" gplugin "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/model" "net/rpc" "os" + "sagooiot/pkg/plugins/model" "time" ) @@ -18,7 +20,7 @@ func init() { // Protocol 协议解析插件接口 type Protocol interface { Info() model.PluginInfo - Encode(args interface{}) model.JsonRes + Encode(args model.DataReq) model.JsonRes Decode(data model.DataReq) model.JsonRes } @@ -33,15 +35,15 @@ func (p *ProtocolRPC) Info() model.PluginInfo { if err != nil { //希望接口返回错误 //这里没有太多其他选择。 - fmt.Println(err.Error()) + g.Log().Debug(context.Background(), err.Error()) //panic(err) } return resp } -func (p *ProtocolRPC) Encode(args interface{}) model.JsonRes { +func (p *ProtocolRPC) Encode(args model.DataReq) model.JsonRes { var resp model.JsonRes - err := p.Client.Call("Plugin.Encode", new(interface{}), &resp) - fmt.Println(args) + err := p.Client.Call("Plugin.Encode", args, &resp) + //fmt.Println(args) if err != nil { //希望接口返回错误 //这里没有太多其他选择。 @@ -78,7 +80,7 @@ func (s *ProtocolRPCServer) Info(args interface{}, resp *model.PluginInfo) error *resp = s.Impl.Info() return nil } -func (s *ProtocolRPCServer) Encode(args interface{}, resp *model.JsonRes) error { +func (s *ProtocolRPCServer) Encode(args model.DataReq, resp *model.JsonRes) error { *resp = s.Impl.Encode(args) return nil } diff --git a/pkg/plugins/noticePlugin.go b/pkg/plugins/noticePlugin.go new file mode 100644 index 0000000..4d5fdea --- /dev/null +++ b/pkg/plugins/noticePlugin.go @@ -0,0 +1,11 @@ +package plugins + +import ( + "sagooiot/pkg/plugins/consts/PluginType" +) + +// GetNoticePlugin 获取通知插件 +func GetNoticePlugin() *SysPlugin { + ins := GetPlugin(PluginType.Notice) + return ins +} diff --git a/pkg/plugins/protocolPlugin.go b/pkg/plugins/protocolPlugin.go new file mode 100644 index 0000000..5baa15b --- /dev/null +++ b/pkg/plugins/protocolPlugin.go @@ -0,0 +1,9 @@ +package plugins + +import "sagooiot/pkg/plugins/consts/PluginType" + +// GetProtocolPlugin 获取协议插件 +func GetProtocolPlugin() *SysPlugin { + ins := GetPlugin(PluginType.Protocol) + return ins +} diff --git a/extend/sdk/sdk.go b/pkg/plugins/sdk/sdk.go similarity index 92% rename from extend/sdk/sdk.go rename to pkg/plugins/sdk/sdk.go index eff669d..30b54d5 100644 --- a/extend/sdk/sdk.go +++ b/pkg/plugins/sdk/sdk.go @@ -3,7 +3,7 @@ package sdk import ( "github.com/gogf/gf/v2/encoding/gjson" "github.com/gogf/gf/v2/encoding/gyaml" - "github.com/sagoo-cloud/sagooiot/extend/model" + "sagooiot/pkg/plugins/model" ) func DecodeNoticeData(data []byte) (res model.NoticeData, err error) { diff --git a/pkg/plugins/sysPlugin.go b/pkg/plugins/sysPlugin.go new file mode 100644 index 0000000..e3518f6 --- /dev/null +++ b/pkg/plugins/sysPlugin.go @@ -0,0 +1,137 @@ +package plugins + +import ( + "context" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/pkg/plugins/consts/PluginType" + "sagooiot/pkg/plugins/model" + "sagooiot/pkg/plugins/module" + "sync" +) + +type SysPlugin struct { + pluginManager *Manager +} + +var ( + onceMap = make(map[string]*sync.Once) + insMap = make(map[string]*SysPlugin) // 用于存放插件类型的单例 + mu sync.Mutex +) + +// GetPlugin 构造方法 +func GetPlugin(pluginType string) *SysPlugin { + mu.Lock() + once, ok := onceMap[pluginType] + if !ok { + once = &sync.Once{} + onceMap[pluginType] = once + } + mu.Unlock() + + once.Do(func() { + instance := &SysPlugin{} + pm, err := pluginInit(pluginType) + if err != nil { + g.Log().Error(context.TODO(), err.Error()) + } + instance.pluginManager = pm + + mu.Lock() + insMap[pluginType] = instance + mu.Unlock() + }) + + return insMap[pluginType] +} + +// GetProtocolByName 获取指定协议名称的插件 +func (pm *SysPlugin) GetProtocolByName(protocolName string) (obj module.Protocol, err error) { + //获取插件 + p, err := pm.pluginManager.GetInterface(protocolName) + if err != nil { + g.Log().Debug(context.Background(), err.Error()) + return + } + obj = p.(module.Protocol) + return +} + +// GetNoticeByName 获取指定通知名称的插件 +func (pm *SysPlugin) GetNoticeByName(noticeName string) (obj module.Notice, err error) { + //获取插件 + p, err := pm.pluginManager.GetInterface(noticeName) + if err != nil { + g.Log().Error(context.Background(), err.Error()) + return + } + obj = p.(module.Notice) + return +} + +// GetProtocolDecodeData 通过协议解析插件处理后,获取解析数据。protocolType 为协议名称 +func (pm *SysPlugin) GetProtocolDecodeData(protocolName string, data []byte) (res model.JsonRes, err error) { + //获取插件 + p, err := pm.pluginManager.GetInterface(protocolName) + if err != nil { + return + } + + var rd = model.DataReq{} + rd.Data = data + resData := p.(module.Protocol).Decode(rd) + return resData, err +} + +// GetProtocolEncodeData 通过协议插件进行编码Encode处理后,获取下发的编码后的数据。protocolType 为协议名称 +func (pm *SysPlugin) GetProtocolEncodeData(protocolName string, reqData model.DataReq) (res model.JsonRes, err error) { + //获取插件 + p, err := pm.pluginManager.GetInterface(protocolName) + if err != nil { + return + } + + //var rd = model.DataReq{} + //rd.Data = data + resData := p.(module.Protocol).Encode(reqData) + return resData, err +} + +// StartPlugin 启动插件 +func (pm *SysPlugin) StartPlugin(pluginId string) (err error) { + return pm.pluginManager.StartPlugin(pluginId) +} + +// StopPlugin 启动插件 +func (pm *SysPlugin) StopPlugin(pluginId string) (err error) { + return pm.pluginManager.StopPlugin(pluginId) +} + +// NoticeSend 通过插件发送通知信息。noticeName 为通知插件名称;msg为通知内容 +func (pm *SysPlugin) NoticeSend(noticeName string, msg model.NoticeInfoData) (res model.JsonRes, err error) { + //获取插件 + p, err := pm.pluginManager.GetInterface(noticeName) + if err != nil { + return + } + + var nd = new(model.NoticeData) + nd.Msg = msg + cfgData, err := getPluginsConfigData(PluginType.Notice, noticeName) + if err != nil { + return + } + nd.Config = cfgData + ndJson := gjson.New(nd) + //转为byte + byteData := ndJson.MustToJson() + + //g.Log().Debug(context.Background(), noticeName, "通知插件发送数据:", byteData) + + res = p.(module.Notice).Send(byteData) + //res, err = gjson.New(sendRes).ToJsonString() + //g.Log().Debug(context.Background(), "发送结果:", res) + + return +} diff --git a/utility/response/response.go b/pkg/response/response.go similarity index 70% rename from utility/response/response.go rename to pkg/response/response.go index 2698592..80858bd 100644 --- a/utility/response/response.go +++ b/pkg/response/response.go @@ -2,12 +2,13 @@ package response import ( "fmt" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/gogf/gf/v2/os/gtime" "io" "net/http" "time" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gtime" ) // JsonRes 数据返回通用JSON数据结构 @@ -36,7 +37,7 @@ func Json(r *ghttp.Request, code int, message string, data ...interface{}) { // JsonExit 返回标准JSON数据并退出当前HTTP执行函数。 func JsonExit(r *ghttp.Request, code int, message string, data ...interface{}) { Json(r, code, message, data...) - r.Exit() + r.ExitAll() } // JsonRedirect 返回标准JSON数据引导客户端跳转。 @@ -56,16 +57,33 @@ func JsonRedirect(r *ghttp.Request, code int, message, redirect string, data ... // JsonRedirectExit 返回标准JSON数据引导客户端跳转,并退出当前HTTP执行函数。 func JsonRedirectExit(r *ghttp.Request, code int, message, redirect string, data ...interface{}) { JsonRedirect(r, code, message, redirect, data...) - r.Exit() + r.ExitAll() } -//ToXls 向前端返回Excel文件 参数 content 为上面生成的io.ReadSeeker, fileTag 为返回前端的文件名 +// ToXls 向前端返回Excel文件 参数 content 为上面生成的io.ReadSeeker, fileTag 为返回前端的文件名 func ToXls(r *ghttp.Request, content io.ReadSeeker, fileTag string) { fileName := fmt.Sprintf("%s%s%s.xlsx", gtime.Now().String(), `-`, fileTag) r.Response.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) - r.Response.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) r.Response.Writer.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") http.ServeContent(r.Response.Writer, r.Request, fileName, time.Now(), content) - r.Exit() + r.ExitAll() + return +} + +// ToPlainText 输出流 +func ToPlainText(r *ghttp.Request, content []byte, fileName string) { + r.Response.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) + r.Response.Writer.Header().Add("Content-Type", "text/plain;charset=UTF-8") + r.Response.Write(content) + r.Response.Flush() +} + +// ToJsonFIle 向前端返回文件 参数 content 为上面生成的io.ReadSeeker, fileTag 为返回前端的文件名 +func ToJsonFIle(r *ghttp.Request, content io.ReadSeeker, fileTag string) { + fileName := fmt.Sprintf("%s%s%s.json", gtime.Now().Format("20060102150405"), `-`, fileTag) + r.Response.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, fileName)) + r.Response.Writer.Header().Add("Content-Type", "application/json") + http.ServeContent(r.Response.Writer, r.Request, fileName, time.Now(), content) + r.ExitAll() return } diff --git a/pkg/statistics/countDeviceData.go b/pkg/statistics/countDeviceData.go new file mode 100644 index 0000000..0dfaf5a --- /dev/null +++ b/pkg/statistics/countDeviceData.go @@ -0,0 +1,64 @@ +package statistics + +import ( + "context" + "sagooiot/internal/consts" + "sagooiot/pkg/cache" + "sagooiot/pkg/utility/utils" + "sync" + "sync/atomic" + "time" +) + +var ( + count int64 + lastDate string + dateMutex sync.Mutex +) + +// CountDeviceData 计数设备数据 +func CountDeviceData() { + currentDateString := utils.GetCurrentDateString() + dateMutex.Lock() + if lastDate != currentDateString { + lastDate = currentDateString + count = 0 + saveCountToDB(0) + } + dateMutex.Unlock() + + atomic.AddInt64(&count, 1) +} + +// GetCurrentDeviceDataCount 获取当前计数 +func GetCurrentDeviceDataCount() int64 { + return atomic.LoadInt64(&count) +} + +// 持久化当前计数 +func saveCountToDB(currentCount int64) { + + err := cache.Instance().Set(context.Background(), consts.AnalysisDeviceCountPrefix+consts.TodayMessageVolume+utils.GetTimeTagGroup()+lastDate, currentCount, 0) + if err != nil { + return + } +} + +// InitCountDeviceData 初始化设备数据计数 +func InitCountDeviceData() { + lastDate = utils.GetCurrentDateString() + // 恢复之前的计数值 + res, err := cache.Instance().Get(context.Background(), consts.AnalysisDeviceCountPrefix+consts.TodayMessageVolume+utils.GetTimeTagGroup()+lastDate) + if err != nil { + return + } + count = res.Int64() + // 设置定时持久化一次 + ticker := time.NewTicker(3 * time.Second) + go func() { + for range ticker.C { + currentCount := atomic.LoadInt64(&count) + saveCountToDB(currentCount) // 持久化当前计数 + } + }() +} diff --git a/pkg/statistics/countDeviceData_test.go b/pkg/statistics/countDeviceData_test.go new file mode 100644 index 0000000..9621bed --- /dev/null +++ b/pkg/statistics/countDeviceData_test.go @@ -0,0 +1,14 @@ +package statistics + +import ( + _ "github.com/gogf/gf/contrib/nosql/redis/v2" + "sagooiot/pkg/utility/utils" + "testing" + "time" +) + +func TestGetDays(t *testing.T) { + year := time.Now().Year() + month := time.Now().Month() + t.Log(utils.CalcDaysFromYearMonth(year, int(month))) +} diff --git a/pkg/tsd/comm/consts.go b/pkg/tsd/comm/consts.go new file mode 100644 index 0000000..c70c48d --- /dev/null +++ b/pkg/tsd/comm/consts.go @@ -0,0 +1,17 @@ +package comm + +const ( + DBTdEngine = "TdEngine" + DBInfluxdb = "Influxdb" + + // td 产品表前缀 + TdProductPrefix = "product_" + // td 设备表前缀 + TdDevicePrefix = "device_" + // td 日志表前缀 + TdLogPrefix = "log_" + // td 属性前缀 + TdPropertyPrefix = "p_" + // td tag前缀 + TdTagPrefix = "t_" +) diff --git a/pkg/tsd/comm/option.go b/pkg/tsd/comm/option.go new file mode 100644 index 0000000..377a4b6 --- /dev/null +++ b/pkg/tsd/comm/option.go @@ -0,0 +1,16 @@ +package comm + +// Option 包含数据库连接的配置选项 +type Option struct { + Host string + Port int + Link string + Org string + Token string + Username string + Password string + Database string + DriverName string // 驱动名称 + MaxIdleConns int // 最大空闲连接数 + MaxOpenConns int // 最大连接数 +} diff --git a/pkg/tsd/comm/tools.go b/pkg/tsd/comm/tools.go new file mode 100644 index 0000000..3b911b2 --- /dev/null +++ b/pkg/tsd/comm/tools.go @@ -0,0 +1,41 @@ +package comm + +import ( + "strings" +) + +// ProductTableName 获取TSD产品表名 +func ProductTableName(key string) string { + // td 表名加前缀,转义中划线 + return TdProductPrefix + strings.ToLower(strings.ReplaceAll(key, "-", "_")) +} + +// DeviceTableName 获取TSD设备表名 +func DeviceTableName(key string) string { + // td 表名加前缀,转义中划线 + return TdDevicePrefix + strings.ToLower(strings.ReplaceAll(key, "-", "_")) +} + +// DeviceLogTable 获取TSD设备日志表名 +func DeviceLogTable(key string) string { + // td 表名加前缀,转义中划线 + return TdLogPrefix + strings.ToLower(strings.ReplaceAll(key, "-", "_")) +} + +// TsdColumnName 属性字段加前缀 +func TsdColumnName(key string) string { + key = strings.ToLower(key) + if key == "ts" { + return "ts" + } + return TdPropertyPrefix + key +} + +// TsdTagName tag字段加前缀 +func TsdTagName(key string) string { + key = strings.ToLower(key) + if key == "device" { + return "device" + } + return TdTagPrefix + key +} diff --git a/pkg/tsd/database.go b/pkg/tsd/database.go new file mode 100644 index 0000000..d8b15dd --- /dev/null +++ b/pkg/tsd/database.go @@ -0,0 +1,35 @@ +package tsd + +import ( + "context" + "database/sql" + "sagooiot/pkg/iotModel" + + "github.com/gogf/gf/v2/database/gdb" +) + +// Database 接口,所有数据库都应该实现这个接口 +type Database interface { + Close() + Query(sql string) (rows *sql.Rows, err error) + Count(table string) (int, error) + InsertLogData(log iotModel.DeviceLog) (result sql.Result, err error) + BatchInsertLogData(deviceLogList map[string][]iotModel.DeviceLog) (resultNum int, err error) + InsertDeviceData(deviceKey string, data iotModel.ReportPropertyData, subKey ...string) (err error) + BatchInsertDeviceData(deviceKey string, deviceDataList []iotModel.ReportPropertyData) (resultNum int, err error) + BatchInsertMultiDeviceData(multiDeviceDataList map[string][]iotModel.ReportPropertyData) (resultNum int, err error) + WatchDeviceData(deviceKey string, callback func(data iotModel.ReportPropertyData)) (err error) + + //获取所有数据库名称 + GetAllDatabaseName(ctx context.Context) (names []string, err error) + //获取指定的数据库下所有的表 + GetTableListByDatabase(ctx context.Context, dbName string) (tableList []iotModel.TsdTables, err error) + //获取指定数据表结构信息 + GetTableInfo(ctx context.Context, tableName string) (table []*iotModel.TsdTableInfo, err error) + //获取指定数据表数据信息 + GetTableData(ctx context.Context, tableName string) (table *iotModel.TsdTableDataInfo, err error) + //获取超级表的单条数据 + GetTableDataOne(ctx context.Context, sqlStr string, args ...any) (rs gdb.Record, err error) + //获取超级表的多条数据 + GetTableDataAll(ctx context.Context, sqlStr string, args ...any) (rs gdb.Result, err error) +} diff --git a/pkg/tsd/factory.go b/pkg/tsd/factory.go new file mode 100644 index 0000000..7be0f9d --- /dev/null +++ b/pkg/tsd/factory.go @@ -0,0 +1,21 @@ +package tsd + +import ( + "fmt" + "sagooiot/pkg/tsd/comm" + "sagooiot/pkg/tsd/internal/influxdb" + "sagooiot/pkg/tsd/internal/tdengine" +) + +// DatabaseFactory 工厂函数,根据数据库类型返回相应的实例 +func DatabaseFactory(dbType string, option comm.Option) Database { + switch dbType { + case comm.DBTdEngine: + return &tdengine.TdEngine{Option: option} + case comm.DBInfluxdb: + return &influxdb.Influxdb{Option: option} + default: + fmt.Println("Unsupported database type.", dbType) + return nil + } +} diff --git a/pkg/tsd/factory_test.go b/pkg/tsd/factory_test.go new file mode 100644 index 0000000..719618a --- /dev/null +++ b/pkg/tsd/factory_test.go @@ -0,0 +1,41 @@ +package tsd + +import ( + "errors" + "sagooiot/pkg/tsd/comm" + "testing" +) + +func TestDatabaseFactory(t *testing.T) { + + // 定义数据库连接选项 + option := comm.Option{ + Database: "sagoo_iot", + Link: "root:taosdata@ws(127.0.0.1:6041)/", + DriverName: "taosWS", + } + + // 使用工厂函数创建 Tdengine 数据库实例 + td := DatabaseFactory(comm.DBTdEngine, option) + + if td == nil { + t.Error(errors.New("factory err")) + } + td.Close() + t.Log("创建 Tdengine 数据库实例成功") + + // 定义数据库连接选项 + option2 := comm.Option{ + Database: "sagoo_iot", + Link: "http://localhost:8086", + Org: "sagoo", + Token: "ez4BQ5QQCUpcAp1FDhhdY9jfcvxq2Z9OLkQSuQG_IPOzE9GvGRHfRm_YYwfuHtCaS7TVefxhEnzCOHi_nGtsCw==", + } + // 使用工厂函数创建 Influxdb 数据库实例 + idb := DatabaseFactory(comm.DBInfluxdb, option2) + if td == nil { + t.Error(errors.New("factory err")) + } + idb.Close() + t.Log("创建 Influxdb 数据库实例成功") +} diff --git a/pkg/tsd/instance.go b/pkg/tsd/instance.go new file mode 100644 index 0000000..eb69de0 --- /dev/null +++ b/pkg/tsd/instance.go @@ -0,0 +1,50 @@ +package tsd + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/pkg/tsd/comm" + "sync" +) + +var ( + instances Database + once sync.Once +) + +// DB 返回一个数据库类型连接的单例 +func DB() Database { + once.Do(func() { + instances = GetDB() + }) + return instances +} + +// GetDB 返回数据库实例 +func GetDB() (db Database) { + var option = comm.Option{} + databaseType := g.Cfg().MustGet(context.Background(), "tsd.database", "TdEngine").String() + switch databaseType { + case comm.DBTdEngine: + link := g.Cfg().MustGet(context.Background(), "tsd.tdengine.dsn", "root:taosdata@ws(127.0.0.1:6041)/") + driverName := g.Cfg().MustGet(context.Background(), "tsd.tdengine.type", "taosWS") + dbName := g.Cfg().MustGet(context.Background(), "tsd.tdengine.dbName", "sagoo_iot") + option = comm.Option{ + Database: dbName.String(), + Link: link.String(), + DriverName: driverName.String(), + } + case comm.DBInfluxdb: + link := g.Cfg().MustGet(context.Background(), "tsd.influxdb.addr", "http://localhost:8086") + org := g.Cfg().MustGet(context.Background(), "tsd.influxdb.org", "sagoo") + dbName := g.Cfg().MustGet(context.Background(), "tsd.influxdb.dbName", "sagooiot") + token := g.Cfg().MustGet(context.Background(), "tsd.influxdb.token", "") + option = comm.Option{ + Database: dbName.String(), + Link: link.String(), + Org: org.String(), + Token: token.String(), + } + } + return DatabaseFactory(databaseType, option) +} diff --git a/pkg/tsd/instance_test.go b/pkg/tsd/instance_test.go new file mode 100644 index 0000000..d20e880 --- /dev/null +++ b/pkg/tsd/instance_test.go @@ -0,0 +1,144 @@ +package tsd + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "sagooiot/internal/consts" + "sagooiot/pkg/iotModel" + "testing" + "time" + + _ "github.com/taosdata/driver-go/v3/taosRestful" + _ "github.com/taosdata/driver-go/v3/taosWS" +) + +func TestGetTableListByDatabase(t *testing.T) { + db := DB() + tableList, err := db.GetTableListByDatabase(context.Background(), "sagoo_iot") + if err != nil { + t.Error(err) + } + t.Log(tableList) + +} + +func TestGetDatabaseInstance(t *testing.T) { + db := DB() + rows, err := db.Query("select * from device_log LIMIT 10") + if err != nil { + t.Fatal(err) + } + + for rows.Next() { + var log iotModel.DeviceLog + err = rows.Scan(&log.Ts, &log.Type, &log.Content, &log.Device) + if err != nil { + t.Fatal(err) + } + t.Log(log) + } + + num, err := DB().Count("device_log") + if err != nil { + t.Fatal(err) + } + t.Log("device 日志数:", num) + +} + +func TestCount(t *testing.T) { + num, err := DB().Count("device_log") + if err != nil { + t.Fatal(err) + } + t.Log(num) +} + +// 插入单条日志数据 +func TestInsertLog(t *testing.T) { + logData := iotModel.DeviceLog{ + Device: "yxjc012311290004983", + Type: "属性上报", + Content: `{"device_id":"yxjc012311290004983","return_time":"2022-11-10 10:49:33","property_99":2,"property_98":2,"property_97":3,"property_96":4,"property_95":2}`, + } + _, err := DB().InsertLogData(logData) + if err != nil { + t.Fatal(err) + } +} + +// 批量插入日志数据 +func TestBatchInsertLogData(t *testing.T) { + var logList []iotModel.DeviceLog + for i := 0; i < 10; i++ { + logData := iotModel.DeviceLog{ + Device: "yxjc012311290004983", + Type: "属性上报", + Content: `{"device_id":"yxjc012311290004983","return_time":"2022-11-10 10:49:33","property_99":2,"property_98":2,"property_97":3,"property_96":4,"property_95":2}`, + } + logList = append(logList, logData) + } + + var upData = make(map[string][]iotModel.DeviceLog) + upData["yxjc012311290004983"] = logList + num, err := DB().BatchInsertLogData(upData) + if err != nil { + t.Fatal(err) + } + t.Log("插入数据条数:", num) +} + +// 获取设备数据 +func TestGetDeviceData(t *testing.T) { + db := DB() + + sqlStr := fmt.Sprintf("select * from device_%s", "t202200001") + rows, err := db.Query(sqlStr) + if err != nil { + t.Fatal(err) + } + defer rows.Close() + + columns, _ := rows.Columns() + var rs gdb.Result + + for rows.Next() { + values := make([]any, len(columns)) + for i := range values { + values[i] = new(any) + } + + err = rows.Scan(values...) + if err != nil { + t.Error(err) + } + + m := make(gdb.Record, len(columns)) + for i, c := range columns { + // 去除前缀 + if c[:2] == consts.TdPropertyPrefix { + c = c[2:] + } + m[c] = toTime(gvar.New(values[i])) + } + rs = append(rs, m) + } + t.Log(rs) +} + +// Time REST连接时区处理 +func toTime(v *g.Var) (rs *g.Var) { + driver := g.Cfg().MustGet(context.TODO(), "tdengine.type") + if driver.String() == "taosRestful" { + if t, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", v.String()); err == nil { + rs = gvar.New(t.Local().Format("2006-01-02 15:04:05")) + return + } + } + + rs = v + return +} diff --git a/pkg/tsd/internal/influxdb/Influxdb.go b/pkg/tsd/internal/influxdb/Influxdb.go new file mode 100644 index 0000000..c2d9c99 --- /dev/null +++ b/pkg/tsd/internal/influxdb/Influxdb.go @@ -0,0 +1,82 @@ +package influxdb + +import ( + "context" + "database/sql" + "github.com/gogf/gf/v2/database/gdb" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/tsd/comm" +) + +type Influxdb struct { + Option comm.Option + Database string +} + +type ReportReq struct { + Timestamp int64 // + Metric string + Dimensions map[string]string + Value float64 +} + +func (m *Influxdb) client() { + + return +} + +func (m *Influxdb) Close() { + return +} +func (m *Influxdb) Query(sql string) (rows *sql.Rows, err error) { + + return +} +func (m *Influxdb) Count(table string) (int, error) { + + return 50, nil +} +func (m *Influxdb) InsertLogData(log iotModel.DeviceLog) (result sql.Result, err error) { + + return + +} +func (m *Influxdb) BatchInsertLogData(ddeviceLogList map[string][]iotModel.DeviceLog) (resultNum int, err error) { + + return +} + +// GetAllDatabaseName 获取所有数据库名称 +func (m *Influxdb) GetAllDatabaseName(ctx context.Context) (names []string, err error) { + + return +} + +// GetTableListByDatabase 获取指定的数据库下所有的表 +func (m *Influxdb) GetTableListByDatabase(ctx context.Context, dbName string) (tableList []iotModel.TsdTables, err error) { + + return +} + +// GetTableInfo 获取指定数据表结构信息 +func (m *Influxdb) GetTableInfo(ctx context.Context, tableName string) (table []*iotModel.TsdTableInfo, err error) { + return +} + +// GetTableData 获取指定数据表数据信息 +func (m *Influxdb) GetTableData(ctx context.Context, tableName string) (table *iotModel.TsdTableDataInfo, err error) { + + return +} + +// GetTableDataOne 获取超级表的单条数据 +func (m *Influxdb) GetTableDataOne(ctx context.Context, sqlStr string, args ...any) (rs gdb.Record, err error) { + + return +} + +// GetTableDataAll 获取超级表的多条数据 +func (m *Influxdb) GetTableDataAll(ctx context.Context, sqlStr string, args ...any) (rs gdb.Result, err error) { + + return +} diff --git a/pkg/tsd/internal/influxdb/device.go b/pkg/tsd/internal/influxdb/device.go new file mode 100644 index 0000000..5d314c5 --- /dev/null +++ b/pkg/tsd/internal/influxdb/device.go @@ -0,0 +1,27 @@ +package influxdb + +import ( + "sagooiot/pkg/iotModel" +) + +// InsertDeviceData 插入设备数据 +func (m *Influxdb) InsertDeviceData(deviceKey string, data iotModel.ReportPropertyData, subKey ...string) (err error) { + + return +} + +// BatchInsertDeviceData 批量插入单设备的数据 +func (m *Influxdb) BatchInsertDeviceData(deviceKey string, deviceDataList []iotModel.ReportPropertyData) (resultNum int, err error) { + return +} + +// BatchInsertMultiDeviceData 批量插入多设备的数据 +func (m *Influxdb) BatchInsertMultiDeviceData(multiDeviceDataList map[string][]iotModel.ReportPropertyData) (resultNum int, err error) { + return +} + +// 监听设备数据日志 +func (m *Influxdb) WatchDeviceData(deviceKey string, callback func(data iotModel.ReportPropertyData)) (err error) { + + return +} diff --git a/pkg/tsd/internal/tdengine/device.go b/pkg/tsd/internal/tdengine/device.go new file mode 100644 index 0000000..81e4909 --- /dev/null +++ b/pkg/tsd/internal/tdengine/device.go @@ -0,0 +1,257 @@ +package tdengine + +import ( + "context" + "database/sql" + "errors" + "fmt" + "github.com/gogf/gf/v2/container/gvar" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/tsd/comm" + "time" + + "strings" +) + +// InsertDeviceData 插入设备数据 +func (m *TdEngine) InsertDeviceData(deviceKey string, data iotModel.ReportPropertyData, subKey ...string) (err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return err + } + } + + if len(data) == 0 { + err = errors.New("数据为空") + return + } + + field := getDeviceField(data) + value := getDeviceValue(data) + + table := comm.DeviceTableName(deviceKey) + if len(subKey) > 0 { + // 子设备 + table = comm.DeviceTableName(subKey[0]) + } + var baseSQL = "INSERT INTO " + table + " (" + strings.Join(field, ",") + ") VALUES" + sqlStr := baseSQL + fmt.Sprintf(" (%s)", strings.Join(value, ",")) + _, err = m.db.Exec(sqlStr) + return +} + +// getDeviceField 获取设备数据字段 +func getDeviceField(data iotModel.ReportPropertyData) []string { + var field = []string{"ts"} + + for k := range data { + k = comm.TsdColumnName(k) + field = append(field, k) + // 属性上报时间 + field = append(field, k+"_time") + } + return field +} + +// getDeviceValue 获取设备数据值 +func getDeviceValue(data iotModel.ReportPropertyData) []string { + //ts := time.Now().Format("Y-m-d H:i:s") + //var value = []string{"'" + ts + "'"} + var value []string + for _, v := range data { + value = append(value, "'"+gvar.New(v.Value).String()+"'") + value = append(value, "'"+gtime.New(v.CreateTime).Format("Y-m-d H:i:s")+"'") + } + return value +} + +// BatchInsertDeviceData 批量插入单设备的数据 +func (m *TdEngine) BatchInsertDeviceData(deviceKey string, deviceDataList []iotModel.ReportPropertyData) (resultNum int, err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return + } + } + if len(deviceDataList) == 0 { + err = errors.New("数据为空") + return + } + table := comm.DeviceTableName(deviceKey) + field := getDeviceField(deviceDataList[0]) + var ( + ts = time.Now().UnixMilli() // Unix 毫秒时间戳 + baseSQL = "INSERT INTO " + table + " (" + strings.Join(field, ",") + ") VALUES" + sqlBuilder strings.Builder + allCount int + allTime int64 + ) + sqlBuilder.WriteString(baseSQL) + //g.Log().Debug(context.Background(), "====04====BatchInsertDeviceData 接收到:", len(deviceDataList), deviceDataList) + + for i, row := range deviceDataList { + ts++ + value := getDeviceValue(row) //获取设备数据值 + sqlBuilder.WriteString(fmt.Sprintf(" (%s)", "'"+time.UnixMilli(ts).Format(time.RFC3339Nano)+"',"+strings.Join(value, ","))) + // 当 SQL 字符串长度超过 15K 或在最后一条数据时执行 + if sqlBuilder.Len() > 15*1024 || i == len(deviceDataList)-1 { + trimmedSQL := strings.TrimRight(sqlBuilder.String(), " ") + start := time.Now() // 开始时间 + g.Log().Debug(context.Background(), "====06====BatchInsertDeviceData SQL:", trimmedSQL) + //_, err := m.db.Exec(trimmedSQL) + if err != nil { + g.Log().Error(context.Background(), err.Error(), trimmedSQL) + } + duration := time.Since(start).Milliseconds() // 执行时间 + executedCount := i + 1 - allCount // 执行条数 + //fmt.Printf("%d, %dms\n", executedCount, duration) + allCount += executedCount // 总条数 + allTime += duration // 总时间 + sqlBuilder.Reset() // 重置 sqlBuilder + sqlBuilder.WriteString(baseSQL) + } + } + resultNum = allCount + //g.Log().Debugf(context.Background(), "Total: %d, Time: %dms\n", allCount, allTime) + return +} + +// BatchInsertMultiDeviceData 插入多设备的数据 +func (m *TdEngine) BatchInsertMultiDeviceData(multiDeviceDataList map[string][]iotModel.ReportPropertyData) (resultNum int, err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return + } + } + if len(multiDeviceDataList) == 0 { + err = errors.New("数据为空") + return + } + + var ( + ts = time.Now().UnixMilli() // Unix 毫秒时间戳 + baseSQL = "INSERT INTO" + sqlBuilder strings.Builder + allCount int + allTime int64 + ) + sqlBuilder.WriteString(baseSQL) + + i := 0 + for deviceKey, deviceData := range multiDeviceDataList { + + table := comm.DeviceTableName(deviceKey) + field := getDeviceField(deviceData[0]) + + ts++ + + for _, data := range deviceData { + value := getDeviceValue(data) + sqlBuilder.WriteString(" " + table + " (" + strings.Join(field, ",") + ") VALUES" + fmt.Sprintf(" (%s)", "'"+time.UnixMilli(ts).Format(time.RFC3339Nano)+"',"+strings.Join(value, ","))) + + } + // 当 SQL 字符串长度超过 15K 或在最后一条数据时执行 + if sqlBuilder.Len() > 15*1024 || i == len(multiDeviceDataList)-1 { + trimmedSQL := strings.TrimRight(sqlBuilder.String(), " ") + start := time.Now() // 开始时间 + //g.Log().Debug(context.Background(), "====06====BatchInsertDeviceData SQL:", trimmedSQL) + _, err := m.db.Exec(trimmedSQL) + if err != nil { + g.Log().Error(context.Background(), err.Error(), trimmedSQL) + } + duration := time.Since(start).Milliseconds() // 执行时间 + executedCount := i + 1 - allCount // 执行条数 + //fmt.Printf("%d, %dms\n", executedCount, duration) + allCount += executedCount // 总条数 + allTime += duration // 总时间 + sqlBuilder.Reset() // 重置 sqlBuilder + sqlBuilder.WriteString(baseSQL) + } + i++ + } + resultNum = allCount + //g.Log().Debugf(context.Background(), "Total: %d, Time: %dms\n", allCount, allTime) + return +} + +// WatchDeviceData 监听设备数据日志 +func (m *TdEngine) WatchDeviceData(deviceKey string, callback func(data iotModel.ReportPropertyData)) (err error) { + + return +} + +// InsertLogData 插入日志数据 +func (m *TdEngine) InsertLogData(log iotModel.DeviceLog) (result sql.Result, err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return + } + } + table := comm.DeviceLogTable(log.Device) + baseSQL := "INSERT INTO %s USING device_log TAGS ('%s') VALUES ('%s', '%s', '%s')" + sqlStr := fmt.Sprintf(baseSQL, table, log.Device, time.Now().Format(time.RFC3339Nano), log.Type, log.Content) + _, err = m.db.Exec(sqlStr) + + return +} + +// BatchInsertLogData 批量插入日志数据 +func (m *TdEngine) BatchInsertLogData(deviceLogList map[string][]iotModel.DeviceLog) (resultNum int, err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return + } + } + if len(deviceLogList) == 0 { + return + } + //g.Log().Debug(context.Background(), "====BatchInsertLogData===接收到 =========", len(deviceLogList)) + + var ( + ts = time.Now().UnixMilli() // Unix 毫秒时间戳 + baseSQL = "INSERT INTO " + sqlBuilder strings.Builder + allCount int + allTime int64 + ) + sqlBuilder.WriteString(baseSQL) + + i := 0 + for k, row := range deviceLogList { + i++ + table := comm.DeviceLogTable(k) + tableSql := fmt.Sprintf("%s USING device_log TAGS ('%s') VALUES ", table, k) + sqlBuilder.WriteString(tableSql) + ts++ + for _, d := range row { + sqlBuilder.WriteString(fmt.Sprintf("('%s', '%s', '%s') ", time.UnixMilli(ts).Format(time.RFC3339Nano), d.Type, d.Content)) + } + // 当 SQL 字符串长度超过 15K 或在最后一条数据时执行 + if sqlBuilder.Len() > 15*1024 || i == len(row) { + trimmedSQL := strings.TrimRight(sqlBuilder.String(), " ") + start := time.Now() // 开始时间 + fmt.Println("====写入TD==》》》", sqlBuilder.Len(), len(deviceLogList), trimmedSQL) + _, err = m.db.Exec(trimmedSQL) + if err != nil { + g.Log().Error(context.Background(), err.Error()) + } + duration := time.Since(start).Milliseconds() // 执行时间 + executedCount := i - allCount // 执行条数 + //fmt.Printf("%d, %dms\n", executedCount, duration) + allCount += executedCount // 总条数 + allTime += duration // 总时间 + sqlBuilder.Reset() // 重置 sqlBuilder + sqlBuilder.WriteString(baseSQL) + resultNum = allCount + } + } + + //fmt.Printf("Total: %d, Time: %dms\n", allCount, allTime) + return +} diff --git a/pkg/tsd/internal/tdengine/tdengine.go b/pkg/tsd/internal/tdengine/tdengine.go new file mode 100644 index 0000000..e10078c --- /dev/null +++ b/pkg/tsd/internal/tdengine/tdengine.go @@ -0,0 +1,289 @@ +package tdengine + +import ( + "context" + "database/sql" + "fmt" + "sagooiot/pkg/iotModel" + "sagooiot/pkg/tsd/comm" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + _ "github.com/taosdata/driver-go/v3/taosRestful" + _ "github.com/taosdata/driver-go/v3/taosWS" +) + +type TdEngine struct { + Option comm.Option + db *sql.DB +} + +// Connect 连接数据库 +func (m *TdEngine) connect() (db *sql.DB, err error) { + tdDb, err := sql.Open(m.Option.DriverName, m.Option.Link+m.Option.Database) + if err != nil { + fmt.Println("failed to connect TDengine, err:", err) + return + } + tdDb.SetMaxIdleConns(m.Option.MaxIdleConns) + tdDb.SetMaxOpenConns(m.Option.MaxOpenConns) + m.db = tdDb + //fmt.Println("Connected to TdEngine database.", m.Option) + return m.db, nil +} +func (m *TdEngine) Close() { + if m.db == nil { + return + } + err := m.db.Close() + if err != nil { + fmt.Println("failed to close TDengine, err:", err) + return + } + //fmt.Println("Closed TdEngine database connection.") +} + +// Query 查询 +func (m *TdEngine) Query(sql string) (rows *sql.Rows, err error) { + if m.db == nil { + _, err = m.connect() + if err != nil { + return + } + } + + rows, err = m.db.Query(sql) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + return +} + +// Count 统计表中数据条数 +func (m *TdEngine) Count(table string) (int, error) { + if m.db == nil { + _, err := m.connect() + if err != nil { + return 0, err + } + } + sqlStr := "select count(*) as num from " + table + rows, err := m.db.Query(sqlStr) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return 0, err + } + var num int + for rows.Next() { + err = rows.Scan(&num) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return 0, err + } + } + return num, nil +} + +// GetAllDatabaseName 获取所有数据库名称 +func (m *TdEngine) GetAllDatabaseName(ctx context.Context) (names []string, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + sqlStr := "show databases" + rows, err := m.db.Query(sqlStr) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + for rows.Next() { + var name string + if err = rows.Scan(&name); err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + names = append(names, name) + } + return +} + +// GetTableListByDatabase 获取指定的数据库下所有的表 +func (m *TdEngine) GetTableListByDatabase(ctx context.Context, dbName string) (tableList []iotModel.TsdTables, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + sqlStr := fmt.Sprintf("select table_name as tableName, db_name as dbName, create_time as createTime, stable_name as stableName from information_schema.ins_tables where db_name='%s'", dbName) + rows, err := m.db.Query(sqlStr) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + for rows.Next() { + var ( + dbName, stableName, tableName string + createTime *gtime.Time + ) + if err = rows.Scan(&tableName, &dbName, &createTime, &stableName); err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + tableList = append(tableList, iotModel.TsdTables{ + DbName: dbName, + StableName: stableName, + TableName: tableName, + CreateTime: createTime, + }) + } + return +} + +// GetTableInfo 获取指定数据表结构信息 +func (m *TdEngine) GetTableInfo(ctx context.Context, tableName string) (table []*iotModel.TsdTableInfo, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + sqlStr := fmt.Sprintf("describe %s", tableName) + rows, err := m.db.Query(sqlStr) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + for rows.Next() { + tableInfo := new(iotModel.TsdTableInfo) + if err = rows.Scan(&tableInfo.Field, &tableInfo.Type, &tableInfo.Length, &tableInfo.Note); err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + table = append(table, tableInfo) + } + return +} + +// GetTableData 获取指定数据表数据信息 +func (m *TdEngine) GetTableData(ctx context.Context, tableName string) (table *iotModel.TsdTableDataInfo, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + sqlStr := fmt.Sprintf("select * from %s", tableName) + rows, err := m.db.Query(sqlStr) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + var ( + columns []string + filed []string + ) + //获取查询结果字段 + if columns, err = rows.Columns(); err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + //封装scanArg + scanArgs := make([]any, len(columns)) + for i := range columns { + filed = append(filed, columns[i]) + scanArgs[i] = &columns[i] + } + table = new(iotModel.TsdTableDataInfo) + table.Filed = append(table.Filed, filed...) + for rows.Next() { + if err = rows.Scan(scanArgs...); err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + //封装返回结果 + var resultMap = make(map[string]interface{}) + for i := range columns { + resultMap[filed[i]] = columns[i] + } + table.Info = append(table.Info, resultMap) + } + return +} + +// GetTableDataOne 获取超级表的单条数据 +func (m *TdEngine) GetTableDataOne(ctx context.Context, sqlStr string, args ...any) (rs gdb.Record, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + rows, err := m.db.Query(sqlStr, args...) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + list, err := g.DB().GetCore().RowsToResult(ctx, rows) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + if !list.IsEmpty() { + rs = make(gdb.Record) + for k, v := range list[0] { + // 去除前缀 + prefixLen := len(comm.TdPropertyPrefix) + if k[:prefixLen] == comm.TdPropertyPrefix { + k = k[prefixLen:] + } + rs[k] = v + } + } + return +} + +// GetTableDataAll 获取超级表的多条数据 +func (m *TdEngine) GetTableDataAll(ctx context.Context, sqlStr string, args ...any) (rs gdb.Result, err error) { + if m.db == nil { + if _, err = m.connect(); err != nil { + return + } + } + + rows, err := m.db.Query(sqlStr, args...) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + + list, err := g.DB().GetCore().RowsToResult(ctx, rows) + if err != nil { + fmt.Println("failed to query TDengine, err:", err) + return + } + if !list.IsEmpty() { + for _, rc := range list { + newRc := make(gdb.Record) + for k, v := range rc { + // 去除前缀 + prefixLen := len(comm.TdPropertyPrefix) + if k[:prefixLen] == comm.TdPropertyPrefix { + k = k[prefixLen:] + } + newRc[k] = v + } + rs = append(rs, newRc) + } + } + return +} diff --git a/pkg/utility/excel.go b/pkg/utility/excel.go new file mode 100644 index 0000000..9cca1a8 --- /dev/null +++ b/pkg/utility/excel.go @@ -0,0 +1,207 @@ +package utility + +import ( + "bytes" + "context" + "fmt" + "io" + "log" + "os" + "path/filepath" + "reflect" + "strconv" + "strings" + "time" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/tealeg/xlsx" + "github.com/xuri/excelize/v2" +) + +// ToMultiSheetExcel 生成io.ReadSeeker 参数 titleList 为Excel表头,dataList 为数据,data 键为sheet +func ToMultiSheetExcel(data map[string][]any) (content io.ReadSeeker) { + // 生成一个新的文件 + file := xlsx.NewFile() + + for sheet, dataList := range data { + // 添加sheet页 + sheet, _ := file.AddSheet(sheet) + // 插入表头 + titleRow := sheet.AddRow() + + //获取表头 + objType := reflect.TypeOf(dataList[0]) + elem := objType.Elem() + var titleList []string + if elem.Kind() == reflect.Struct { + for i := 1; i <= elem.NumField(); i++ { + field := elem.Field(i - 1) + if field.Name != "PageReq" { + if field.Tag != "" && field.Tag.Get("dc") != "" { + titleList = append(titleList, g.I18n().T(context.TODO(), field.Tag.Get("dc"))) + } else { + titleList = append(titleList, g.I18n().T(context.TODO(), field.Name)) + } + } + } + } + + for _, v := range titleList { + cell := titleRow.AddCell() + cell.Value = v + //表头字体颜色 + cell.GetStyle().Font.Color = "000000" + cell.GetStyle().Fill.BgColor = "cfe2f3" + //居中显示 + cell.GetStyle().Alignment.Horizontal = "center" + cell.GetStyle().Alignment.Vertical = "center" + } + // 插入内容 + for _, v := range dataList { + row := sheet.AddRow() + row.WriteStruct(v, -1) + } + } + + var buffer bytes.Buffer + _ = file.Write(&buffer) + content = bytes.NewReader(buffer.Bytes()) + return +} + +// ToExcel 生成io.ReadSeeker 参数 titleList 为Excel表头,dataList 为数据 +func ToExcel(dataList []interface{}) (content io.ReadSeeker) { + // 生成一个新的文件 + file := xlsx.NewFile() + // 添加sheet页 + sheet, _ := file.AddSheet("Sheet1") + // 插入表头 + titleRow := sheet.AddRow() + + //获取表头 + objType := reflect.TypeOf(dataList[0]) + elem := objType.Elem() + var titleList []string + if elem.Kind() == reflect.Struct { + for i := 1; i <= elem.NumField(); i++ { + field := elem.Field(i - 1) + if field.Name != "PageReq" { + if field.Tag != "" && field.Tag.Get("description") != "" { + titleList = append(titleList, g.I18n().T(context.TODO(), field.Tag.Get("description"))) + } else { + titleList = append(titleList, g.I18n().T(context.TODO(), field.Name)) + } + } + } + } + + for _, v := range titleList { + cell := titleRow.AddCell() + cell.Value = v + //表头字体颜色 + cell.GetStyle().Font.Color = "000000" + cell.GetStyle().Fill.BgColor = "cfe2f3" + //居中显示 + cell.GetStyle().Alignment.Horizontal = "center" + cell.GetStyle().Alignment.Vertical = "center" + } + // 插入内容 + for _, v := range dataList { + row := sheet.AddRow() + row.WriteStruct(v, -1) + } + + var buffer bytes.Buffer + _ = file.Write(&buffer) + content = bytes.NewReader(buffer.Bytes()) + return +} + +func DownloadExcel(titleList []string, dataList []interface{}, filename ...string) (string, error) { + curDir, err := os.Getwd() + + if err != nil { + return "", err + } + var fileName string + if len(filename) > 0 && filename[0] != "" { + fileName = filename[0] + } else { + curdate := time.Now().UnixNano() + fileName = strconv.FormatInt(curdate, 10) + ".xls" + } + filePath := curDir + "/public/upload/" + fileName + + err = CreateFilePath(filePath) + if err != nil { + log.Printf("%s", err.Error()) + return "", err + } + + // 生成一个新的文件 + file := xlsx.NewFile() + // 添加sheet页 + sheet, _ := file.AddSheet("Sheet1") + // 插入表头 + titleRow := sheet.AddRow() + for _, v := range titleList { + cell := titleRow.AddCell() + cell.Value = v + } + // 插入内容 + for _, v := range dataList { + row := sheet.AddRow() + row.WriteStruct(v, -1) + } + + // 在提供的路径中将文件保存到xlsx文件 + err = file.Save(filePath) + if err != nil { + return "", err + } + return fileName, nil +} + +// CreateFilePath 创建路径 +func CreateFilePath(filePath string) error { + // 路径不存在创建路径 + path, _ := filepath.Split(filePath) // 获取路径 + _, err := os.Stat(path) // 检查路径状态,不存在创建 + if err != nil || os.IsExist(err) { + err = os.MkdirAll(path, os.ModePerm) + } + return err +} + +// ReadExcelFile 读取EXCEL文件 +func ReadExcelFile(file *ghttp.UploadFile, tableName ...string) (rows [][]string, err error) { + //获取文件名字 + fileName := file.Filename + //获取文件后缀 + var fileSuffix = fileName[strings.LastIndex(fileName, ".")+1:] + //判断文件名字是否为.xlsx + if !strings.EqualFold(fileSuffix, "xlsx") { + err = gerror.New("文件类型错误") + return + } + //读取EXCEL文件内容 + open, err := file.Open() + if err != nil { + fmt.Println("打开文件失败") + } + f, err := excelize.OpenReader(open) + if err != nil { + return nil, err + } + //默认读取第一个 + firstSheet := "" + if len(tableName) > 0 { + firstSheet = tableName[0] + } else { + firstSheet = f.GetSheetName(0) + } + rows, err = f.GetRows(firstSheet) + return rows, err +} diff --git a/pkg/utility/helper/event.go b/pkg/utility/helper/event.go new file mode 100644 index 0000000..474f18d --- /dev/null +++ b/pkg/utility/helper/event.go @@ -0,0 +1,51 @@ +package helper + +import ( + "context" + "sync" +) + +type EventFunc func(ctx context.Context, args ...interface{}) + +type sEvent struct { + sync.Mutex + list map[string][]EventFunc // 所有事件的列表 +} + +var event *sEvent + +// Event 事件实例 +func Event() *sEvent { + if event == nil { + event = &sEvent{ + list: make(map[string][]EventFunc), + } + } + return event +} + +// Register 往一个分组中注册事件 +func (e *sEvent) Register(group string, callback EventFunc) { + e.Lock() + defer e.Unlock() + e.list[group] = append(e.list[group], callback) +} + +// Call 回调一个分组的事件 +func (e *sEvent) Call(group string, ctx context.Context, args ...interface{}) { + if events, ok := e.list[group]; ok { + for _, f := range events { + f(ctx, args...) + } + } +} + +// Remove 移动一个分组的事件 +func (e *sEvent) Remove(group string) { + delete(e.list, group) +} + +// Clear 清空事件列表 +func (e *sEvent) Clear() { + e.list = make(map[string][]EventFunc) +} diff --git a/pkg/utility/helper/helper.go b/pkg/utility/helper/helper.go new file mode 100644 index 0000000..44a2f33 --- /dev/null +++ b/pkg/utility/helper/helper.go @@ -0,0 +1,26 @@ +package helper + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/grpool" +) + +// AppName 应用名称 +func AppName(ctx context.Context) string { + return g.Cfg().MustGet(ctx, "system.name", "sagooiot").String() +} + +// SafeGo 安全的调用协程,发生错误时输出错误日志,不抛出panic +func SafeGo(ctx context.Context, f func(ctx context.Context)) { + err := grpool.AddWithRecover(ctx, func(ctx context.Context) { + f(ctx) + }, func(ctx context.Context, err error) { + g.Log().Errorf(ctx, "SafeGo exec failed:%+v", err) + }) + + if err != nil { + g.Log().Errorf(ctx, "SafeGo AddWithRecover err:%+v", err) + return + } +} diff --git a/utility/notifier/notifier.go b/pkg/utility/notifier/notifier.go similarity index 94% rename from utility/notifier/notifier.go rename to pkg/utility/notifier/notifier.go index 251ea3c..422b7d0 100644 --- a/utility/notifier/notifier.go +++ b/pkg/utility/notifier/notifier.go @@ -88,24 +88,25 @@ func (p *Notifier) getState() *State { return p.state } -// 标记通知已发送状态 +// 标记通知已发送状态 func (p *Notifier) sent() { now := time.Now() p.getState().sentAt = &now } -// 判断通知器是否为空状态(未触发过的状态) +// 判断通知器是否为空状态(未触发过的状态) func (p *Notifier) empty() bool { return p.getState().sentAt == nil } -// 重置通知器状态 +// 重置通知器状态 func (p *Notifier) clear() { p.getState().sentAt = nil p.getState().lastRemindedAt = nil } -// 获取是否到达发送提醒的时间点 +// 获取是否到达发送提醒的时间点 +// // 获取再次触发后,静默周期将从头计算。 // 后续的触发动作有必要保证触发成功,否则将在下个周期获得重新触发的机会 func (p *Notifier) remind() bool { diff --git a/utility/notifier/notifier_test.go b/pkg/utility/notifier/notifier_test.go similarity index 100% rename from utility/notifier/notifier_test.go rename to pkg/utility/notifier/notifier_test.go diff --git a/pkg/utility/nx/nx.go b/pkg/utility/nx/nx.go new file mode 100644 index 0000000..313fc79 --- /dev/null +++ b/pkg/utility/nx/nx.go @@ -0,0 +1,95 @@ +package nx + +import ( + "context" + "errors" + "time" +) + +type Nx struct { + ops Options + valid bool +} + +// New 创建一个新的 nx 锁实例 +func New(options ...func(*Options)) (nx *Nx) { + ops := getOptionsOrSetDefault(nil) + for _, f := range options { + f(ops) + } + nx = &Nx{ + ops: *ops, + valid: ops.redis != nil, + } + if err := ops.Validate(); err != nil { + return + } + return +} + +// MustLock 尝试获取锁,如果获取失败,则在 interval 秒内自动重试 retry 次以获得锁定,如果失败,则返回错误 +func (nx Nx) MustLock(c ...context.Context) (err error) { + if !nx.valid { + return + } + ctx := context.Background() + if len(c) > 0 { + ctx = c[0] + } + var attempts int + for { + ok, err := nx.tryLock(ctx) + if err != nil { + return err + } + if ok { + return nil + } + //MustLock将在 interval 秒内自动重试 retry 次以获得锁定,如果失败,则返回错误 + if attempts >= nx.ops.retry { + err = errors.New("lock timeout") + return err + } + time.Sleep(nx.ops.interval) + attempts++ + } +} + +// tryLock 尝试获取锁。如果获取成功,则返回 true,否则返回 false +func (nx Nx) tryLock(ctx context.Context) (bool, error) { + cmd := nx.ops.redis.SetNX(ctx, nx.ops.key, 1, time.Duration(nx.ops.expire)*time.Second) + _, err := cmd.Result() + if err != nil { + if err == nil || err.Error() == "redis: nil" { + return false, nil + } + return false, err + } + return true, nil +} + +// Lock 尝试获取锁。如果获取成功,则返回 true,否则返回 false +func (nx Nx) Lock(c ...context.Context) (ok bool) { + if !nx.valid { + return + } + ctx := context.Background() + if len(c) > 0 { + ctx = c[0] + } + ok, _ = nx.tryLock(ctx) + return +} + +// Unlock 释放锁 +func (nx Nx) Unlock(ctx ...context.Context) { + if !nx.valid { + return + } + c := context.Background() + if len(ctx) > 0 { + c = ctx[0] + } + nx.ops.redis.Del(c, nx.ops.key) + return +} diff --git a/pkg/utility/nx/options.go b/pkg/utility/nx/options.go new file mode 100644 index 0000000..667206d --- /dev/null +++ b/pkg/utility/nx/options.go @@ -0,0 +1,63 @@ +package nx + +import ( + "errors" + "github.com/redis/go-redis/v9" + "time" +) + +type Options struct { + redis redis.UniversalClient + key string //redis缓存密钥,默认nx.lock + expire int //密钥过期时间,默认为1分钟,避免死锁,不应设置太长 + retry int //重试次数,默认10次 + interval time.Duration //重试间隔,默认25毫秒 +} + +// Validate 校验 Options 结构体的参数是否合法 +func (o *Options) Validate() error { + if o.redis == nil { + return errors.New("redis must not be nil") + } + return nil +} + +// getOptionsOrSetDefault 获取 Options 结构体的指针,如果参数为 nil,则返回默认值 +func getOptionsOrSetDefault(options *Options) *Options { + if options == nil { + return &Options{ + key: "nx.lock", + expire: 60, + retry: 10, + interval: 25 * time.Millisecond, + } + } + return options +} + +// WithRedis 设置 redis 客户端 +func WithRedis(rd redis.UniversalClient) func(*Options) { + return func(options *Options) { + if rd != nil { + getOptionsOrSetDefault(options).redis = rd + } + } +} + +// WithKey 设置 redis 缓存密钥 +func WithKey(key string) func(*Options) { + return func(options *Options) { + if key != "" { + getOptionsOrSetDefault(options).key = key + } + } +} + +// WithExpire 设置 redis 缓存密钥过期时间 +func WithExpire(second int) func(*Options) { + return func(options *Options) { + if second > 0 { + getOptionsOrSetDefault(options).expire = second + } + } +} diff --git a/pkg/utility/param/param.go b/pkg/utility/param/param.go new file mode 100644 index 0000000..1e3b0cc --- /dev/null +++ b/pkg/utility/param/param.go @@ -0,0 +1,22 @@ +package param + +import "fmt" + +func ConvertMapToSlice(data []map[string]interface{}) []map[string]interface{} { + var transformed []map[string]interface{} + for _, item := range data { + transformedItem := make(map[string]interface{}) + for key, valueMap := range item { + if subMap, ok := valueMap.(map[string]interface{}); ok { + for subKey, subValue := range subMap { + if val, ok := subValue.(float64); ok { + transformedKey := fmt.Sprintf("%s_%s", key, subKey) + transformedItem[transformedKey] = val + } + } + } + } + transformed = append(transformed, transformedItem) + } + return transformed +} diff --git a/pkg/utility/simple/simple.go b/pkg/utility/simple/simple.go new file mode 100644 index 0000000..0b51628 --- /dev/null +++ b/pkg/utility/simple/simple.go @@ -0,0 +1,51 @@ +package simple + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/os/grpool" + "github.com/gogf/gf/v2/util/gconv" +) + +// SafeGo 安全的调用协程,遇到错误时输出错误日志而不是抛出panic +func SafeGo(ctx context.Context, f func(ctx context.Context), lv ...interface{}) { + var level = glog.LEVEL_ERRO + if len(lv) > 0 { + level = gconv.Int(lv[0]) + } + + err := grpool.AddWithRecover(ctx, func(ctx context.Context) { + f(ctx) + }, func(ctx context.Context, err error) { + Logf(level, ctx, "SafeGo exec failed:%+v", err) + }) + + if err != nil { + Logf(level, ctx, "SafeGo AddWithRecover err:%+v", err) + return + } +} + +func Logf(level int, ctx context.Context, format string, v ...interface{}) { + switch level { + case glog.LEVEL_DEBU: + g.Log().Debugf(ctx, format, v...) + case glog.LEVEL_INFO: + g.Log().Infof(ctx, format, v...) + case glog.LEVEL_NOTI: + g.Log().Noticef(ctx, format, v...) + case glog.LEVEL_WARN: + g.Log().Warningf(ctx, format, v...) + case glog.LEVEL_ERRO: + g.Log().Errorf(ctx, format, v...) + case glog.LEVEL_CRIT: + g.Log().Criticalf(ctx, format, v...) + case glog.LEVEL_PANI: + g.Log().Panicf(ctx, format, v...) + case glog.LEVEL_FATA: + g.Log().Fatalf(ctx, format, v...) + default: + g.Log().Errorf(ctx, format, v...) + } +} diff --git a/pkg/utility/utils/data_test.go b/pkg/utility/utils/data_test.go new file mode 100644 index 0000000..ea09405 --- /dev/null +++ b/pkg/utility/utils/data_test.go @@ -0,0 +1,28 @@ +package utils + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" + "testing" +) + +func TestGetWeekDay(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a, b := GetWeekDay() + g.Dump(a, b) + }) +} + +func TestGetQuarterDay(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a, b := GetQuarterDay() + g.Dump(a, b) + }) +} + +func TestGetBetweenDates(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + a := GetBetweenDates("2022-1-5", "2023-1-20") + g.Dump(a) + }) +} diff --git a/pkg/utility/utils/date.go b/pkg/utility/utils/date.go new file mode 100644 index 0000000..810c3ff --- /dev/null +++ b/pkg/utility/utils/date.go @@ -0,0 +1,238 @@ +package utils + +import ( + "fmt" + "github.com/gogf/gf/v2/os/gtime" + "sagooiot/internal/consts" + "strconv" + "time" +) + +// GetWeekDay 获取本周的开始时间和结束时间 +func GetWeekDay() (string, string) { + now := time.Now() + start := now.Truncate(24*time.Hour).AddDate(0, 0, int(time.Monday-now.Weekday())).Format("2006-01-02") + " 00:00:00" + end := now.Truncate(24*time.Hour).AddDate(0, 0, int(time.Monday-now.Weekday())+6).Format("2006-01-02") + " 23:59:59" + return start, end +} + +// GetBetweenDates 根据开始日期和结束日期计算出时间段内所有日期 +// 参数为日期格式,如:2020-01-01 +func GetBetweenDates(sdate, edate string) []string { + var d []string + timeFormatTpl := "2006-01-02" + if len(timeFormatTpl) != len(sdate) { + timeFormatTpl = timeFormatTpl[0:len(sdate)] + } + date, err := time.Parse(timeFormatTpl, sdate) + if err != nil { + // 时间解析,异常 + return d + } + date2, err := time.Parse(timeFormatTpl, edate) + if err != nil { + // 时间解析,异常 + return d + } + if date2.Before(date) { + // 如果结束时间小于开始时间,异常 + return d + } + // 输出日期格式固定 + timeFormatTpl = "2006-01-02" + date2Str := date2.Format(timeFormatTpl) + d = append(d, date.Format(timeFormatTpl)) + for { + date = date.AddDate(0, 0, 1) + dateStr := date.Format(timeFormatTpl) + d = append(d, dateStr) + if dateStr == date2Str { + break + } + } + return d +} + +func GetHourBetweenDates(sdate, edate string) []string { + var d []string + date := gtime.New(sdate) + date2 := gtime.New(edate) + date2Str := date2.Format("Y-m-d H:00:00") + + d = append(d, date.Format("Y-m-d H:00:00")) + for { + date = gtime.New(date).Add(time.Hour) + dateStr := date.Format("Y-m-d H:00:00") + d = append(d, dateStr) + if dateStr == date2Str { + break + } + } + return d +} + +// GetQuarterDay 获得当前季度的初始和结束日期 +func GetQuarterDay() (string, string) { + now := time.Now() + year, quarter := now.Year(), (now.Month()-1)/3+1 + start := time.Date(year, (quarter-1)*3+1, 1, 0, 0, 0, 0, time.Local).Format("2006-01-02 15:04:05") + end := time.Date(year, (quarter-1)*3+1+2, daysIn((quarter-1)*3+1+2, year), 23, 59, 59, 0, time.Local).Format("2006-01-02 15:04:05") + return start, end +} +func daysIn(m time.Month, year int) int { + return time.Date(year, m+1, 0, 0, 0, 0, 0, time.UTC).Day() +} + +// GetTimeByType 根据类型获取开始时间、结束时间及差值 1 天 2 周 3 月 4 年 +func GetTimeByType(types int) (index int, begin string, end string) { + switch types { + case 1: + begin = gtime.Now().Format("Y-m-d 00:00:00") + end = gtime.Now().Format("Y-m-d H:i:s") + index = gtime.Now().Hour() + 1 + break + case 2: + //begin, _ = GetWeekDay() + begin = gtime.Now().AddDate(0, 0, -7).Format("Y-m-d 00:00:00") + + end = gtime.Now().Format("Y-m-d H:i:s") + index = int(gtime.New(end).Sub(gtime.New(begin)).Hours()/24) + 1 + break + case 3: + //begin = gtime.Now().Format("Y-m-01 00:00:00") + begin = gtime.Now().AddDate(0, 0, -30).Format("Y-m-d 00:00:00") + //end = gtime.Now().AddDate(0, 1, 0).Format("Y-m-01 00:00:00") + end = gtime.Now().Format("Y-m-d H:i:s") + //index = gtime.Now().Day() + index = int(gtime.New(end).Sub(gtime.New(begin)).Hours()/24) + 1 + break + case 4: + begin = gtime.Now().Format("Y-01-01 00:00:00") + end = gtime.Now().AddDate(1, 0, 0).Format("Y-01-01 00:00:00") + index = gtime.Now().Month() + break + default: + begin = gtime.Now().Format("Y-m-d 00:00:00") + end = gtime.Now().Format("Y-m-d H:i:s") + index = gtime.Now().Hour() + 1 + break + } + return +} + +// GetTime 根据类型和开始时间获取时间段及长度 +func GetTime(i int, types int, begin string) (startTime string, endTime string, duration int, unit string) { + switch types { + case 1: + h, _ := time.ParseDuration(strconv.Itoa(i) + "h") + startTime = gtime.New(begin).Add(h).Format("Y-m-d H:i:s") + endTime = gtime.New(startTime).Add(time.Hour).Format("Y-m-d H:i:s") + duration = gtime.New(startTime).Hour() + unit = "时" + break + case 2, 3: + startTime = gtime.New(begin).AddDate(0, 0, i).Format("Y-m-d H:i:s") + endTime = gtime.New(startTime).AddDate(0, 0, 1).Format("Y-m-d H:i:s") + duration = gtime.New(startTime).Day() + unit = "日" + case 4: + startTime = gtime.New(begin).AddDate(0, i, 0).Format("Y-m-d H:i:s") + endTime = gtime.New(startTime).AddDate(0, 1, 0).Format("Y-m-d H:i:s") + duration = gtime.New(startTime).Month() + unit = "月" + break + default: + startTime = gtime.New(begin).Add(time.Duration(i)).Format("Y-m-d H:i:s") + endTime = gtime.New(startTime).Add(time.Hour).Format("Y-m-d H:i:s") + duration = gtime.New(startTime).Hour() + unit = "时" + break + } + return +} + +// CalcDaysFromYearMonth 返回给定年份和月份的天数 +func CalcDaysFromYearMonth(year int, month int) int { + // 获取指定月份的第一天 + firstOfMonth := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) + // 获取下一个月的第一天 + firstOfNextMonth := firstOfMonth.AddDate(0, 1, 0) + // 获取当前月的最后一天 + lastDayOfMonth := firstOfNextMonth.AddDate(0, 0, -1) + // 返回当前月份的天数 + return lastDayOfMonth.Day() +} + +// GetTimeUnix 转为时间戳->秒数 +func GetTimeUnix(t time.Time) int64 { + return t.Unix() +} + +// GetTimeMills 转为时间戳->毫秒数 +func GetTimeMills(t time.Time) int64 { + return t.UnixNano() / 1e6 +} + +// GetTimeByInt 时间戳转时间 +func GetTimeByInt(t1 int64) time.Time { + return time.Unix(t1, 0) +} + +// GetHourDiffer 计算俩个时间差多少小时 +func GetHourDiffer(startTime, endTime string) float64 { + var hour float64 + t1, err := time.ParseInLocation(consts.TimeFormatCompact, startTime, time.Local) + t2, err := time.ParseInLocation(consts.TimeFormatCompact, endTime, time.Local) + if err == nil && CompareTime(t1, t2) { + diff := GetTimeUnix(t2) - GetTimeUnix(t1) + hour = float64(diff) / 3600 + return hour + } + return hour +} + +// GetMinutesDiffer 计算俩个时间差多少分钟 +func GetMinutesDiffer(startTime, endTime string) int { + // 两个时间点 + t1 := gtime.NewFromStr(startTime) + t2 := gtime.NewFromStr(endTime) + + // 计算时间差并转换为分钟数 + diff := t2.Sub(t1) + return int(diff.Minutes()) +} + +// CompareTime 比较两个时间大小 +func CompareTime(t1, t2 time.Time) bool { + return t1.Before(t2) +} + +// IsSameDay 是否为同一天 +func IsSameDay(t1, t2 int64) bool { + y1, m1, d1 := time.Unix(t1, 0).Date() + y2, m2, d2 := time.Unix(t2, 0).Date() + return y1 == y2 && m1 == m2 && d1 == d2 +} + +// IsSameMinute 是否为同一分钟 +func IsSameMinute(t1, t2 int64) bool { + d1 := time.Unix(t1, 0).Format("2006-01-02 15:04") + d2 := time.Unix(t2, 0).Format("2006-01-02 15:04") + return d1 == d2 +} + +// GetCurrentDateString 获取当前日期字符串 +func GetCurrentDateString() string { + return time.Now().Format("2006-01-02") +} + +// GetTimeTagGroup 获取当前日期字符串,结果:2024:05 +func GetTimeTagGroup() string { + // 获取当前时间 + now := time.Now() + // 获取当前年份 + year := now.Year() + // 获取当前月份 + month := now.Month() + return fmt.Sprintf("%d:%02d:", year, month) +} diff --git a/pkg/utility/utils/file_utils.go b/pkg/utility/utils/file_utils.go new file mode 100644 index 0000000..544d415 --- /dev/null +++ b/pkg/utility/utils/file_utils.go @@ -0,0 +1,180 @@ +package utils + +import ( + "archive/zip" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/net/ghttp" + "io" + "os" + "path" + "path/filepath" + "strings" +) + +// ReadZipFileByFileName 读取压缩包指定文件内容 +func ReadZipFileByFileName(file *ghttp.UploadFile, fileName string) (result map[string]interface{}, err error) { + if fileName == "" { + err = gerror.New("文件名称不能为空") + return + } + src, err := file.Open() + if err != nil { + return + } + // 打开zip文件 对于macOS生成的zip文件,需要去掉前面的__MACOSX目录 + var zr *zip.Reader + zr, err = zip.NewReader(src, file.Size) + if err != nil { + return + } + + var zipFile *zip.File + for _, f := range zr.File { + if !f.FileInfo().IsDir() && f.FileInfo().Name() == fileName { + zipFile = f + break + } + } + + if zipFile == nil { + err = gerror.Newf("无指定%s文件,请确认后再上传!", fileName) + return + } + + var fileJson io.ReadCloser + fileJson, err = zipFile.Open() + if err != nil { + err = gerror.Newf("打开文件失败:%s", err) + return + } + + // 读取JSON文件数据 + var data []byte + data, err = io.ReadAll(fileJson) + if err != nil { + err = gerror.Newf("读取文件失败:%s", err) + return + } + + err = json.Unmarshal(data, &result) + if err != nil { + err = gerror.Newf("解析JSON数据失败:%s", err) + return + } + return +} + +// UploadZip 上传ZIP包 +func UploadZip(file *ghttp.UploadFile, zipPath string, excludeFiles []string) (err error) { + + src, err := file.Open() + if err != nil { + return + } + // 打开zip文件 对于macOS生成的zip文件,需要去掉前面的__MACOSX目录 + var zr *zip.Reader + zr, err = zip.NewReader(src, file.Size) + if err != nil { + return + } + + //上传路径 + if !FileIsExisted(zipPath) { + // 文件夹不存在,创建文件夹 + if err = os.MkdirAll(zipPath, os.ModePerm); err != nil { + return + } + } + + type closer func() + var cleanups []closer + defer func() { + for _, cleanup := range cleanups { + cleanup() + } + }() + + for _, f := range zr.File { + // 去掉__MACOSX目录 + if strings.Contains(f.Name, "__MACOSX") { + continue + } + + // 判断是否为文件夹 + if f.FileInfo().IsDir() || ShouldExclude(f.Name, excludeFiles) { + continue + } + + var rc io.ReadCloser + rc, err = f.Open() + if err != nil { + return + } + cleanups = append(cleanups, func() { rc.Close() }) + + destFilePath := filepath.Join(zipPath, filepath.Base(f.Name)) + if f.FileInfo().IsDir() { + if err = os.MkdirAll(destFilePath, os.ModePerm); err != nil { + return + } + } else { + var destFile *os.File + destFile, err = os.OpenFile(destFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return + } + cleanups = append(cleanups, func() { destFile.Close() }) + + _, err = io.Copy(destFile, rc) + if err != nil { + return + } + } + } + return +} + +func ShouldExclude(filename string, excludeList []string) bool { + baseName := filepath.Base(filename) + for _, excludedFile := range excludeList { + if baseName == excludedFile { + return true + } + } + return false +} + +// WriteToFile 写入文件 +func WriteToFile(fileName string, content string) error { + f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) + if err != nil { + return err + } + n, _ := f.Seek(0, io.SeekEnd) + _, err = f.WriteAt([]byte(content), n) + defer func(f *os.File) { + if err := f.Close(); err != nil { + fmt.Println(err) + } + }(f) + return err +} + +// FileIsExisted 文件或文件夹是否存在 +func FileIsExisted(filename string) bool { + existed := true + if _, err := os.Stat(filename); os.IsNotExist(err) { + existed = false + } + return existed +} + +// ParseFilePath 解析路径获取文件名称及后缀 +func ParseFilePath(pathStr string) (fileName string, fileType string) { + fileNameWithSuffix := path.Base(pathStr) + fileType = path.Ext(fileNameWithSuffix) + fileName = strings.TrimSuffix(fileNameWithSuffix, fileType) + return +} diff --git a/pkg/utility/utils/ip.go b/pkg/utility/utils/ip.go new file mode 100644 index 0000000..3bc51fd --- /dev/null +++ b/pkg/utility/utils/ip.go @@ -0,0 +1,92 @@ +package utils + +import ( + "fmt" + "log" + "net" + "strings" +) + +// IpInBlackListRange 判断IP是否在黑名单 +func IpInBlackListRange(ip string, ipList []string) (result bool) { + result = false + cidr := strings.Join(ipList[:], ",") + cidrs, err := ParseIpRange(cidr) + if err != nil { + log.Println("Error:", err) + return + } + return isInRangeList(ip, cidrs) +} + +// isInRange 判断IP是否在指定的范围 +// 支持单个IP,支持多个IP,多IP时需要用“,”隔开 +// 支持IP段,如192.168.0.1/24 +// 支持IP范围,格式如:192.168.1.xx-192.168.1.xx +func isInRange(ip, cidr string) bool { + _, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + return false + } + return ipnet.Contains(net.ParseIP(ip)) +} + +// isInRangeList 判断一个 IP 地址是否在多个 CIDR 范围内 +func isInRangeList(ip string, cidrs []string) bool { + for _, cidr := range cidrs { + if isInRange(ip, cidr) { + return true + } + } + return false +} + +// ParseIpRange 用于将以 "," 分割的多个 CIDR 范围字符串解析为 CIDR 数组。 +// 如果一个 CIDR 范围是以 "-" 分割的两个 IP 地址,那么我们会使用 binaryToInt 和 intToIP 函数将它们转换为整数并再次转换为 CIDR 字符串, +// 并将其加入到 CIDR 数组中。 +func ParseIpRange(cidr string) ([]string, error) { + var cidrs []string + + parts := strings.Split(cidr, ",") + for _, part := range parts { + part = strings.TrimSpace(part) + if strings.Contains(part, "-") { + ips := strings.Split(part, "-") + startIP := net.ParseIP(ips[0]).To4() + endIP := net.ParseIP(ips[1]).To4() + if startIP == nil || endIP == nil { + return nil, fmt.Errorf("无效IP范围: %s", part) + } + start := binaryToInt(startIP) + end := binaryToInt(endIP) + for i := start; i <= end; i++ { + startIP := net.ParseIP(intToIP(i).String()) + if startIP == nil { + return nil, fmt.Errorf("无效IP地址: %s", intToIP(i).String()) + } + + cidrs = append(cidrs, intToIP(i).String()+"/32") + } + } else { + startIP := net.ParseIP(part) + if startIP == nil { + return nil, fmt.Errorf("无效IP地址: %s", part) + } + cidrs = append(cidrs, part) + } + } + return cidrs, nil +} + +func binaryToInt(ip net.IP) int { + return int(ip[0])<<24 | int(ip[1])<<16 | int(ip[2])<<8 | int(ip[3]) +} + +func intToIP(n int) net.IP { + b := make([]byte, 4) + b[0] = byte(n >> 24) + b[1] = byte(n >> 16) + b[2] = byte(n >> 8) + b[3] = byte(n) + return net.IPv4(b[0], b[1], b[2], b[3]) +} diff --git a/pkg/utility/utils/ip_test.go b/pkg/utility/utils/ip_test.go new file mode 100644 index 0000000..edc1504 --- /dev/null +++ b/pkg/utility/utils/ip_test.go @@ -0,0 +1,24 @@ +package utils + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" + "testing" +) + +func TestIsInRange(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + //ipList := []string{ + // "192.168.0.1/32", + // "192.168.0.10-192.168.1.200", + // "192.168.aaa.100", + //} + ipList := []string{ + "192.168.0.1/32", + "192.168.0.10-192.168.1.200", + } + + ip := "192.168.1.100" + g.Dump(IpInBlackListRange(ip, ipList)) + }) +} diff --git a/pkg/utility/utils/rsa_utils.go b/pkg/utility/utils/rsa_utils.go new file mode 100644 index 0000000..b244202 --- /dev/null +++ b/pkg/utility/utils/rsa_utils.go @@ -0,0 +1,245 @@ +package utils + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "os" + + "github.com/gogf/gf/v2/errors/gerror" +) + +// GenerateKeyPair 生成RSA密钥对 +func GenerateKeyPair(bits int, privateKeyFile string, publicKeyFile string) (err error) { + privateKey, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + err = gerror.New("生成RSA密钥对时发生错误") + return + } + + err = SavePrivateKeyToFile(privateKey, privateKeyFile) + if err != nil { + err = gerror.New("保存私钥文件时发生错误") + return + } + + publicKey := &privateKey.PublicKey + err = SavePublicKeyToFile(publicKey, publicKeyFile) + if err != nil { + err = gerror.New("保存公钥文件时发生错误") + return + } + + return +} + +// EncryptPKCS1v15 使用公钥加密数据 +func EncryptPKCS1v15(publicKeyFile string, content string) (ciphertext string, err error) { + publicKeyBytes, err := os.ReadFile(publicKeyFile) + if err != nil { + err = gerror.New("读取公钥文件时发生错误:" + err.Error()) + return + } + + publicKey, err := ParsePublicKeyFromPEM(publicKeyBytes) + if err != nil { + err = gerror.New("解析公钥时发生错误:" + err.Error()) + return + } + + ciphertextBytes, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, []byte(content)) + if err != nil { + err = gerror.New("加密数据时发生错误:" + err.Error()) + return + } + ciphertext = base64.StdEncoding.EncodeToString(ciphertextBytes) + return +} + +// DecryptPKCS1v15 使用私钥解密数据 +func DecryptPKCS1v15(privateKeyFile, ciphertext string) (plaintext string, err error) { + privateKeyBytes, err := os.ReadFile(privateKeyFile) + if err != nil { + err = gerror.New("读取私钥文件时发生错误:" + err.Error()) + return + } + + privateKey, err := ParsePrivateKeyFromPEM(privateKeyBytes) + if err != nil { + err = gerror.New("解析私钥时发生错误:" + err.Error()) + return + } + ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return + } + plaintextBytes, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, ciphertextBytes) + if err != nil { + err = gerror.New("解密数据时发生错误:" + err.Error()) + return + } + plaintext = string(plaintextBytes) + return +} + +// EncryptOAEP 使用公钥加密数据 +func EncryptOAEP(publicKeyFile string, content string) (ciphertext string, err error) { + publicKeyBytes, err := os.ReadFile(publicKeyFile) + if err != nil { + err = gerror.New("读取公钥文件时发生错误:" + err.Error()) + return + } + + publicKey, err := ParsePublicKeyFromPEM(publicKeyBytes) + if err != nil { + err = gerror.New("解析公钥时发生错误:" + err.Error()) + return + } + + ciphertextBytes, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, []byte(content), nil) + if err != nil { + err = gerror.New("加密数据时发生错误:" + err.Error()) + return + } + ciphertext = base64.StdEncoding.EncodeToString(ciphertextBytes) + return +} + +// DecryptOAEP 使用私钥解密数据 +func DecryptOAEP(privateKeyFile, ciphertext string) (plaintext string, err error) { + privateKeyBytes, err := os.ReadFile(privateKeyFile) + if err != nil { + err = gerror.New("读取私钥文件时发生错误:" + err.Error()) + return + } + + privateKey, err := ParsePrivateKeyFromPEM(privateKeyBytes) + if err != nil { + err = gerror.New("解析私钥时发生错误:" + err.Error()) + return + } + ciphertextBytes, err := base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return + } + plaintextBytes, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, ciphertextBytes, nil) + if err != nil { + err = gerror.New("解密数据时发生错误:" + err.Error()) + return + } + plaintext = string(plaintextBytes) + return +} + +// SavePrivateKeyToFile 将 RSA 私钥保存到文件 +func SavePrivateKeyToFile(privateKey *rsa.PrivateKey, filename string) (err error) { + keyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + return + } + privateBlock := &pem.Block{ + Type: "PRIVATE KEY", + Bytes: keyBytes, + } + privatePem := pem.EncodeToMemory(privateBlock) + err = os.WriteFile(filename, privatePem, 0600) + if err != nil { + err = gerror.New("保存私钥文件时发生错误:" + err.Error()) + return + } + return +} + +// SavePublicKeyToFile 将 RSA 公钥保存到文件 +func SavePublicKeyToFile(publicKey *rsa.PublicKey, filename string) (err error) { + keyBytes, err := x509.MarshalPKIXPublicKey(publicKey) + publicBlock := &pem.Block{ + Type: "PUBLIC KEY", + Bytes: keyBytes, + } + publicPem := pem.EncodeToMemory(publicBlock) + err = os.WriteFile(filename, publicPem, 0644) + if err != nil { + err = gerror.New("保存公钥文件时发生错误:" + err.Error()) + return + } + return +} + +// ParsePrivateKeyFromPEM 从 PEM 格式的字节切片中解析 RSA 私钥 +func ParsePrivateKeyFromPEM(pemBytes []byte) (privateKey *rsa.PrivateKey, err error) { + block, _ := pem.Decode(pemBytes) + if block == nil || block.Type != "PRIVATE KEY" { + err = gerror.New("无效的私钥") + return + } + PKCS8PrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + err = gerror.New("解析私钥时发生错误:" + err.Error()) + return + } + privateKey, ok := PKCS8PrivateKey.(*rsa.PrivateKey) + if !ok { + err = gerror.New("解析私钥时发生错误:invalid public key type") + return + } + return +} + +// ParsePublicKeyFromPEM 从 PEM 格式的字节切片中解析 RSA 公钥 +func ParsePublicKeyFromPEM(pemBytes []byte) (publicKey *rsa.PublicKey, err error) { + block, _ := pem.Decode(pemBytes) + if block == nil || block.Type != "PUBLIC KEY" { + err = gerror.New("无效的公钥") + return + } + + pKIXPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + err = gerror.New("解析公钥时发生错误:" + err.Error()) + return + } + + publicKey, ok := pKIXPublicKey.(*rsa.PublicKey) + if !ok { + err = gerror.New("解析公钥时发生错误:invalid public key type") + return + } + + return +} + +// Decrypt 使用私钥解密数据 +func Decrypt(privateKeyFile, ciphertext string, types string) (plaintext string, err error) { + switch types { + case "OAEP": + plaintext, err = DecryptOAEP(privateKeyFile, ciphertext) + break + case "PKCS1v15": + plaintext, err = DecryptPKCS1v15(privateKeyFile, ciphertext) + break + default: + plaintext, err = DecryptPKCS1v15(privateKeyFile, ciphertext) + break + } + return +} + +// Encrypt 使用公钥加密数据 +func Encrypt(privateKeyFile, ciphertext string, types string) (plaintext string, err error) { + switch types { + case "OAEP": + plaintext, err = EncryptOAEP(privateKeyFile, ciphertext) + break + case "PKCS1v15": + plaintext, err = EncryptPKCS1v15(privateKeyFile, ciphertext) + break + default: + plaintext, err = EncryptPKCS1v15(privateKeyFile, ciphertext) + break + } + return +} diff --git a/pkg/utility/utils/rsa_utils_test.go b/pkg/utility/utils/rsa_utils_test.go new file mode 100644 index 0000000..259d297 --- /dev/null +++ b/pkg/utility/utils/rsa_utils_test.go @@ -0,0 +1,34 @@ +package utils + +import ( + "fmt" + "sagooiot/internal/consts" + "testing" +) + +func TestRsaReq(t *testing.T) { + + /*err := GenerateKeyPair(2048, "../../resource/rsa/private.pem", "../../resource/rsa/public.pem") + if err != nil { + fmt.Println("生成RSA密钥对时发生错误:", err) + return + }*/ + + message := "Zd-Gf3h8_r4" + ciphertext, err := Encrypt("../../resource/rsa/public.pem", message, consts.RsaOAEP) + if err != nil { + fmt.Println("加密数据时发生错误:", err) + return + } + + fmt.Println("加密后的数据:", ciphertext) + + message = ciphertext + plaintext, err := Decrypt("../../resource/rsa/private.pem", message, consts.RsaOAEP) + if err != nil { + fmt.Println("解密数据时发生错误:", err) + return + } + + fmt.Println("解密后的数据:", plaintext) +} diff --git a/utility/utils/template.go b/pkg/utility/utils/template.go similarity index 100% rename from utility/utils/template.go rename to pkg/utility/utils/template.go diff --git a/utility/utils/template_test.go b/pkg/utility/utils/template_test.go similarity index 100% rename from utility/utils/template_test.go rename to pkg/utility/utils/template_test.go diff --git a/pkg/utility/utils/utils.go b/pkg/utility/utils/utils.go new file mode 100644 index 0000000..046ff31 --- /dev/null +++ b/pkg/utility/utils/utils.go @@ -0,0 +1,574 @@ +package utils + +import ( + "bufio" + "context" + "fmt" + "github.com/gogf/gf/v2/errors/gerror" + "io" + "net" + "net/http" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "time" + + "github.com/gogf/gf/v2/crypto/gmd5" + "github.com/gogf/gf/v2/encoding/gcharset" + "github.com/gogf/gf/v2/encoding/gjson" + "github.com/gogf/gf/v2/encoding/gurl" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" +) + +// EncryptPassword 密码加密 +func EncryptPassword(password, salt string) string { + return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt)) +} + +// GetDomain 获取当前请求接口域名 +func GetDomain(ctx context.Context) string { + r := g.RequestFromCtx(ctx) + pathInfo, err := gurl.ParseURL(r.GetUrl(), -1) + if err != nil { + g.Log().Error(ctx, err) + return "" + } + return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"]) +} + +// GetClientIp 获取客户端IP +func GetClientIp(ctx context.Context) string { + return g.RequestFromCtx(ctx).GetClientIp() +} + +// GetLocalIP 获取服务器内网IP +func GetLocalIP() (string, error) { + var localIP string + interfaces, err := net.Interfaces() + if err != nil { + return "", err + } + // 遍历所有网卡 + for _, i := range interfaces { + if i.Flags&net.FlagUp == 0 { + continue // 网卡未开启 + } + if i.Flags&net.FlagLoopback != 0 { + continue // 网卡为loopback地址 + } + addrs, err := i.Addrs() + if err != nil { + return "", err + } + // 遍历网卡上的所有地址 + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip == nil || ip.IsLoopback() { + continue + } + ip = ip.To4() + if ip == nil { + continue // 不是ipv4地址 + } + localIP = ip.String() + return localIP, nil + } + } + return "", fmt.Errorf("no local IP address found") +} + +// GetPublicIP 获取公网IP +func GetPublicIP() (ip string, err error) { + resp, err := http.Get("https://ifconfig.co/ip") + if err != nil { + fmt.Println(err) + return + } + defer func(Body io.ReadCloser) { + if err := Body.Close(); err != nil { + fmt.Println(err) + } + }(resp.Body) + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println(err) + return + } + ip = string(body) + // 去除空格 + ip = strings.Replace(ip, " ", "", -1) + // 去除换行符 + ip = strings.Replace(ip, "\n", "", -1) + + return +} + +// GetUserAgent 获取user-agent +func GetUserAgent(ctx context.Context) string { + return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent") +} + +// GetCityByIp 获取ip所属城市 +func GetCityByIp(ip string) string { + if ip == "" { + return "" + } + if ip == "[::1]" || ip == "127.0.0.1" { + return "内网IP" + } + url := "https://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip + bytes := g.Client().GetBytes(context.TODO(), url) + src := string(bytes) + srcCharset := "GBK" + tmp, _ := gcharset.ToUTF8(srcCharset, src) + json, err := gjson.DecodeToJson(tmp) + if err != nil { + return "" + } + if json.Get("code").Int() == 0 { + city := "" + if strings.EqualFold(json.Get("pro").String(), json.Get("city").String()) { + city = fmt.Sprintf("%s", json.Get("pro").String()) + } else { + city = fmt.Sprintf("%s %s", json.Get("pro").String(), json.Get("city").String()) + } + + return city + } else { + return "" + } +} + +func RemoveRepeatedElementAndEmpty(arr []int) []int { + newArr := make([]int, 0) + for _, item := range arr { + repeat := false + if len(newArr) > 0 { + for _, v := range newArr { + if v == item { + repeat = true + break + } + } + } + if repeat { + continue + } + newArr = append(newArr, item) + } + return newArr +} + +// RemoveDuplicationMap 数组去重 +func RemoveDuplicationMap(arr []string) []string { + set := make(map[string]struct{}, len(arr)) + j := 0 + for _, v := range arr { + _, ok := set[v] + if ok { + continue + } + set[v] = struct{}{} + arr[j] = v + j++ + } + return arr[:j] +} + +// Decimal 保留两位小数 +func Decimal(value float64) float64 { + value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64) + return value +} + +// InArray 判断字符串是否存在数组中 +func InArray(target string, strArray []string) bool { + sort.Strings(strArray) + index := sort.SearchStrings(strArray, target) + if index < len(strArray) && strArray[index] == target { + return true + } + return false +} + +// FileSize 字节的单位转换 保留两位小数 +func FileSize(fileSize int64) string { + units := []string{"B", "KB", "MB", "GB", "TB", "EB"} + var size = float64(fileSize) + var i int + for i = 0; size > 1024; i++ { + size /= 1024 + } + return fmt.Sprintf("%.2f %s", size, units[i]) +} + +type fileInfo struct { + name string + size int64 +} + +// WalkDir 获取目录下文件的名称和大小 +func WalkDir(dirname string) ([]fileInfo, error) { + var fileInfos []fileInfo + err := filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if !info.IsDir() { + fileInfos = append(fileInfos, fileInfo{name: path, size: info.Size()}) + } + return nil + }) + + return fileInfos, err +} + +// DirSize 获取目录下所有文件大小 +func DirSize(dirname string) string { + var ( + s int64 + files, _ = WalkDir(dirname) + ) + for _, n := range files { + s += n.size + } + return FileSize(s) +} + +func ConvertToStringSlice(data []interface{}) []string { + result := make([]string, len(data)) + for i, v := range data { + str, ok := v.(string) + if !ok { + // 如果类型断言失败,可以在此处进行相应的错误处理 + // 这里简单地将该元素转换为空字符串 + str = "" + } + result[i] = str + } + return result +} + +// 删除文件 +func DeleteFile(name string) error { + //判断文件是否存在 + if _, err := os.Stat(name); err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + + //打开文件 + file, err := os.Open(name) + if err != nil { + return err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + fmt.Println(err) + } + }(file) + + //删除文件 + err = os.Remove(name) + if err != nil { + return err + } + + return nil +} + +// ReverseReadLines 从文件末尾开始逆序高效地读取行。 +func ReverseReadLines(name string) ([]string, error) { + // 打开文件。 + file, err := os.Open(name) + if err != nil { + return nil, err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + g.Log().Error(context.Background(), err) + } + }(file) + + // 获取文件大小。 + fs, err := file.Stat() + if err != nil { + return nil, err + } + fileSize := fs.Size() + + // 定义合理的缓冲区大小进行读取。这个值可以根据预期的文件大小和行长度进行调整。 + const bufferSize = 1024 * 20 + + // 创建一个切片用于保存行。 + var lines []string + + // 从文件末尾开始分块读取。 + for offset := fileSize; offset > 0; offset -= bufferSize { + // 计算读取的大小。 + size := GetMin(bufferSize, offset) + buffer := make([]byte, size) + + // 定位并读取这一块。 + _, err := file.ReadAt(buffer, offset-size) + if err != nil { + return nil, err + } + + // 将缓冲区分割成行,并以逆序添加它们。 + scanner := bufio.NewScanner(bufio.NewReaderSize(file, int(size))) + var chunkLines []string + for scanner.Scan() { + chunkLines = append([]string{scanner.Text()}, chunkLines...) + } + // 将这一块的行添加到总行中。 + lines = append(chunkLines, lines...) + if err := scanner.Err(); err != nil { + return nil, err + } + } + + return lines, nil +} + +func ReverseRead(name string, lineNum uint) ([]string, error) { + //打开文件 + file, err := os.Open(name) + if err != nil { + return nil, err + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + g.Log().Error(context.Background(), err) + } + }(file) + //获取文件大小 + fs, err := file.Stat() + if err != nil { + return nil, err + } + fileSize := fs.Size() + + var offset int64 = -1 //偏移量,初始化为-1,若为0则会读到EOF + char := make([]byte, 1) //用于读取单个字节 + lineStr := "" //存放一行的数据 + buff := make([]string, 0, 100) + for (-offset) <= fileSize { + //通过Seek函数从末尾移动游标然后每次读取一个字节 + _, err := file.Seek(offset, io.SeekEnd) + if err != nil { + return nil, err + } + _, err = file.Read(char) + if err != nil { + return buff, err + } + if char[0] == '\n' { + offset-- //windows跳过'\r' + lineNum-- //到此读取完一行 + buff = append(buff, lineStr) + lineStr = "" + if lineNum == 0 { + return buff, nil + } + } else { + lineStr = string(char) + lineStr + } + offset-- + } + buff = append(buff, lineStr) + //使用mahonia解码 + for i := 0; i < len(buff); i++ { + buff[i], err = gcharset.ToUTF8("UTF-8", buff[i]) + } + return buff, nil +} + +// 文件一块一块的读取 +func ReadBlock(filePath string) { + start1 := time.Now() + file, err := os.Open(filePath) + if err != nil { + g.Log().Error(context.Background(), err) + return + } + defer func(file *os.File) { + err := file.Close() + if err != nil { + g.Log().Error(context.Background(), err) + } + }(file) + // 设置每次读取字节数 + buffer := make([]byte, 1024*4) + for { + n, err := file.Read(buffer) + // 控制条件,根据实际调整 + if err != nil && err != io.EOF { + g.Log().Error(context.Background(), err) + } + if n == 0 { + break + } + // 如下代码打印出每次读取的文件块(字节数) + //fmt.Println(string(buffer[:n])) + } + fmt.Println("readBolck spend : ", time.Now().Sub(start1)) +} + +const ( + kilobyte = 1024 + megabyte = 1024 * kilobyte + gigabyte = 1024 * megabyte + terabyte = 1024 * gigabyte + petabyte = 1024 * terabyte +) + +// FormatSize 格式化文件大小。 +func FormatSize(size int64) string { + switch { + case size < kilobyte: + return strconv.Itoa(int(size)) + "B" + case size < megabyte: + return fmt.Sprintf("%.2fK", float64(size)/kilobyte) + case size < gigabyte: + return fmt.Sprintf("%.2fM", float64(size)/megabyte) + case size < terabyte: + return fmt.Sprintf("%.2fG", float64(size)/gigabyte) + case size < petabyte: + return fmt.Sprintf("%.2fT", float64(size)/terabyte) + default: + return fmt.Sprintf("%.2fP", float64(size)/petabyte) + } +} + +func ValidatePassword(password string, minimumLength int, requireComplexity int, requireDigit int, requireLowercase int, requireUppercase int) (flag bool, err error) { + //初始化返回结果 + flag = true + //判断密码长度 + if len(password) < minimumLength { + err = gerror.New(fmt.Sprintf(g.I18n().T(context.TODO(), "{#utilsValidatePwLen}"), minimumLength)) + flag = false + return + } + //是否有复杂字符 + if requireComplexity == 1 && !hasComplexCharacters(password) { + err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwChar}")) + flag = false + return + } + //是否有数字 + if requireDigit == 1 && !hasDigit(password) { + err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwDigit}")) + flag = false + return + } + //是否有小写字母 + if requireLowercase == 1 && !hasLowercaseLetter(password) { + err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwLower}")) + flag = false + return + } + //是否有大写字母 + if requireUppercase == 1 && !hasUppercaseLetter(password) { + err = gerror.New(g.I18n().T(context.TODO(), "{#utilsValidatePwUpper}")) + flag = false + return + } + + return +} + +// hasComplexCharacters:检查字符串中是否有复杂字符(特殊字符) +func hasComplexCharacters(str string) bool { + specialCharacters := "!@#$%^&*()_+-=[]{}|;:,.<>?~" + + for _, char := range str { + if contains(specialCharacters, string(char)) { + return true + } + } + + return false +} + +// hasDigit:检查字符串中是否有数字 +func hasDigit(str string) bool { + for _, char := range str { + if isDigit(string(char)) { + return true + } + } + + return false +} + +// hasLowercaseLetter:检查字符串中是否有小写字母 +func hasLowercaseLetter(str string) bool { + for _, char := range str { + if isLowercase(string(char)) { + return true + } + } + + return false +} + +// hasUppercaseLetter:检查字符串中是否有大写字母 +func hasUppercaseLetter(str string) bool { + for _, char := range str { + if isUppercase(string(char)) { + return true + } + } + + return false +} + +// isDigit:判断字符是否是数字 +func isDigit(c string) bool { + return c >= "0" && c <= "9" +} + +// isLowercase:判断字符是否是小写字母 +func isLowercase(c string) bool { + return c >= "a" && c <= "z" +} + +// isUppercase:判断字符是否是大写字母 +func isUppercase(c string) bool { + return c >= "A" && c <= "Z" +} + +// contains:判断字符串是否包含指定字符 +func contains(str, char string) bool { + for _, c := range str { + if string(c) == char { + return true + } + } + + return false +} + +// GetMin 返回两个整数中的较小值 +func GetMin(a, b int64) int64 { + if a < b { + return a + } + return b +} diff --git a/pkg/utility/utils/utils_test.go b/pkg/utility/utils/utils_test.go new file mode 100644 index 0000000..e47365c --- /dev/null +++ b/pkg/utility/utils/utils_test.go @@ -0,0 +1,52 @@ +package utils + +import ( + "crypto/sha256" + "fmt" + "testing" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/test/gtest" +) + +func TestFileSize(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + size := FileSize(10240000) + g.Dump(size) + }) +} + +func TestGetLocalIP(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ip, _ := GetLocalIP() + g.Dump(ip) + }) +} +func TestGetPublicIP(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + ip, _ := GetPublicIP() + g.Dump(ip) + }) +} + +func TestRead(t *testing.T) { + ct, err := ReverseRead("./t.log", 1000) + if err != nil { + fmt.Println(err) + return + } + for _, v := range ct { + fmt.Println(v) + } +} + +func TestM(t *testing.T) { + // 第一种调用方法 + sum := sha256.Sum256([]byte("hello world\n")) + fmt.Printf("%x\n", sum) + + // 第二种调用方法 + h := sha256.New() + h.Write([]byte("hello world\n")) + fmt.Printf("%x\n", h.Sum(nil)) +} diff --git a/utility/version/version.go b/pkg/utility/version/version.go similarity index 90% rename from utility/version/version.go rename to pkg/utility/version/version.go index d075d4d..3ac7619 100644 --- a/utility/version/version.go +++ b/pkg/utility/version/version.go @@ -18,5 +18,5 @@ func ShowLogo(buildVersion, buildTime, commitID string) { fmt.Println("BuildTime :", buildTime) fmt.Println("CommitID :", commitID) fmt.Println("") - + fmt.Println("Copyright:", "Liaoning Sagoo Cloud Technology Co.,Ltd") } diff --git a/pkg/worker/errors.go b/pkg/worker/errors.go new file mode 100644 index 0000000..3995353 --- /dev/null +++ b/pkg/worker/errors.go @@ -0,0 +1,12 @@ +package worker + +import "fmt" + +var ( + ErrUuidNil = fmt.Errorf("uuid is empty") + ErrRedisNil = fmt.Errorf("redis is empty") + ErrRedisInvalid = fmt.Errorf("redis is invalid") + ErrExprInvalid = fmt.Errorf("expr is invalid") + ErrSaveCron = fmt.Errorf("save cron failed") + ErrHttpCallbackInvalidStatusCode = fmt.Errorf("http callback invalid status code") +) diff --git a/pkg/worker/options.go b/pkg/worker/options.go new file mode 100644 index 0000000..aa2005b --- /dev/null +++ b/pkg/worker/options.go @@ -0,0 +1,256 @@ +package worker + +import ( + "context" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "reflect" + "time" +) + +type Options struct { + group string //任务处理器的组名 + redisUri string //redis连接地址 + redisPeriodKey string //redis周期任务key + retention int //redis周期任务key过期时间 + maxRetry int //任务最大重试次数 + handler func(ctx context.Context, p Payload) error //任务的处理函数 + handlerNeedWorker func(worker Worker, ctx context.Context, p Payload) error //需要Worker参数的任务处理函数 + callback string //任务完成后的回调地址 + clearArchived int //清除已归档任务的时间间隔 + timeout int //任务处理器的超时时间 +} + +// WithGroup 设置任务处理器的组名 +func WithGroup(s string) func(*Options) { + return func(options *Options) { + getOptionsOrSetDefault(options).group = s + } +} + +// WithRedisUri 设置redis连接地址,默认值redis://127.0.0.1:6379/0 +func WithRedisUri(s string) func(*Options) { + return func(options *Options) { + getOptionsOrSetDefault(options).redisUri = s + } +} + +// WithRedisPeriodKey 设置redis周期任务key +func WithRedisPeriodKey(s string) func(*Options) { + return func(options *Options) { + getOptionsOrSetDefault(options).redisPeriodKey = s + } +} + +// WithRetention 成功任务存储时间,默认 60 秒,如果提供此选项,任务将在成功处理后作为已完成任务存储 +func WithRetention(second int) func(*Options) { + return func(options *Options) { + if second > 0 { + getOptionsOrSetDefault(options).retention = second + } + } +} + +// WithMaxRetry 任务出错时的最大重试次数,默认为 3 +func WithMaxRetry(count int) func(*Options) { + return func(options *Options) { + getOptionsOrSetDefault(options).maxRetry = count + } +} + +// WithHandler 设置任务的回调处理器 +func WithHandler(fun func(ctx context.Context, p Payload) error) func(*Options) { + return func(options *Options) { + if fun != nil { + getOptionsOrSetDefault(options).handler = fun + } + } +} + +// WithHandlerNeedWorker 设置需要Worker参数的任务处理函数 +func WithHandlerNeedWorker(fun func(worker Worker, ctx context.Context, p Payload) error) func(*Options) { + return func(options *Options) { + if fun != nil { + getOptionsOrSetDefault(options).handlerNeedWorker = fun + } + } +} + +// WithCallback 设置任务完成后的回调地址 +func WithCallback(s string) func(*Options) { + return func(options *Options) { + getOptionsOrSetDefault(options).callback = s + } +} + +// WithClearArchived 清除已存档任务的间隔,默认为 300 秒 +func WithClearArchived(second int) func(*Options) { + return func(options *Options) { + if second > 0 { + getOptionsOrSetDefault(options).clearArchived = second + } + } +} + +// WithTimeout 任务超时时间,默认为 10 秒 +func WithTimeout(second int) func(*Options) { + return func(options *Options) { + if second > 0 { + getOptionsOrSetDefault(options).timeout = second + } + } +} + +// WithOptions 设置任务处理器的配置 +func getOptionsOrSetDefault(options *Options) *Options { + addr := g.Cfg().MustGet(context.Background(), "redis.default.address").String() + db := g.Cfg().MustGet(context.Background(), "redis.default.db").String() + user := g.Cfg().MustGet(context.Background(), "redis.default.user").String() + if user == "" { + user = "default" + } + pass := g.Cfg().MustGet(context.Background(), "redis.default.pass").String() + redisUri := "redis://127.0.0.1/0" + if pass != "" { + redisUri = fmt.Sprintf("redis://%s:%s@%s/%s", user, pass, addr, db) + } else { + redisUri = fmt.Sprintf("redis://%s/%s", addr, db) + + } + if options == nil { + return &Options{ + group: "task", + redisUri: redisUri, + redisPeriodKey: "period", + retention: 60, + maxRetry: 3, + clearArchived: 300, + timeout: 30, + } + } + return options +} + +type RunOptions struct { + uid string + group string + payload []byte + expr string // only period task + in *time.Duration // only once task + at *time.Time // only once task + now bool // only once task + retention int // only once task + replace bool // only once task + ctx context.Context // only once task + maxRetry int + timeout int +} + +// WithRunUuid 任务唯一id +func WithRunUuid(s string) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).uid = s + } +} + +// WithRunGroup 组前缀,默认组 +func WithRunGroup(s string) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).group = s + } +} + +// WithRunPayload 任务负载,任务回调会使用 +func WithRunPayload(s []byte) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).payload = s + } +} + +// WithRunExpr Cron表达式, 最小单位1分钟, 参见gorhill/cronexpr +func WithRunExpr(s string) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).expr = s + } +} + +// WithRunIn 任务延迟执行,在xxx秒内运行 +func WithRunIn(in time.Duration) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).in = &in + } +} + +// WithRunAt 运行任务的时间 +func WithRunAt(at time.Time) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).at = &at + } +} + +// WithRunNow 立即运行任务 +func WithRunNow(flag bool) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).now = flag + } +} + +// WithRunRetention 任务过期时间,默认60秒 +func WithRunRetention(second int) func(*RunOptions) { + return func(options *RunOptions) { + if second > 0 { + getRunOptionsOrSetDefault(options).retention = second + } + } +} + +// WithRunReplace 当uid重复时,删除旧的并创建新的 +func WithRunReplace(flag bool) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).replace = flag + } +} + +// WithRunCtx 任务上下文 +func WithRunCtx(ctx context.Context) func(*RunOptions) { + return func(options *RunOptions) { + if !interfaceIsNil(ctx) { + getRunOptionsOrSetDefault(options).ctx = ctx + } + } +} + +// WithRunMaxRetry 最大重试次数, 任务回调发生error会重试,默认3次 +func WithRunMaxRetry(count int) func(*RunOptions) { + return func(options *RunOptions) { + getRunOptionsOrSetDefault(options).maxRetry = count + } +} + +// WithRunTimeout 任务超时,默认60秒 +func WithRunTimeout(second int) func(*RunOptions) { + return func(options *RunOptions) { + if second > 0 { + getRunOptionsOrSetDefault(options).timeout = second + } + } +} + +// 获取运行选项或设置默认值 +func getRunOptionsOrSetDefault(options *RunOptions) *RunOptions { + if options == nil { + return &RunOptions{ + group: "group", + timeout: 60, + } + } + return options +} + +func interfaceIsNil(i interface{}) bool { + v := reflect.ValueOf(i) + if v.Kind() == reflect.Ptr { + return v.IsNil() + } + return i == nil +} diff --git a/pkg/worker/task.go b/pkg/worker/task.go new file mode 100644 index 0000000..e36f817 --- /dev/null +++ b/pkg/worker/task.go @@ -0,0 +1,235 @@ +package worker + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/gogf/gf/v2/frame/g" + "github.com/pkg/errors" + + "go.opentelemetry.io/otel" + "io" + "reflect" + "sagooiot/internal/tasks" + "strings" + "sync" +) + +type Tasks struct { + worker *Worker + taskJob tasks.TaskJob +} + +var ( + instance *Tasks + once sync.Once +) + +func TasksInstance() *Tasks { + once.Do(func() { + instance, _ = NewTasks() // Initialize only once + }) + return instance +} + +// Once 注册一个任务以运行一次。 +func (tk Tasks) Once(options ...func(*RunOptions)) error { + return tk.worker.Once(options...) +} + +// Cron 注册一个任务以由cron表达式运行。 +func (tk Tasks) Cron(options ...func(*RunOptions)) error { + return tk.worker.Cron(options...) +} + +// Remove 从任务队列中删除一个任务。 +func (tk Tasks) Remove(ctx context.Context, uid string) error { + return tk.worker.Remove(ctx, uid) +} + +// GetTaskJobNameList 获取任务可用方法名列表。 +func (tk Tasks) GetTaskJobNameList() (res map[string]string) { + // 获取结构体的类型 + taskJobType := reflect.TypeOf(tk.taskJob) + res = make(map[string]string) + // 获取方法的数量 + numMethod := taskJobType.NumMethod() + // 获取每个方法的信息 + for i := 0; i < numMethod; i++ { + // 获取第 i 个方法 + method := taskJobType.Method(i) + // 获取方法的名称 + methodName := method.Name + res[methodName] = tk.taskJob.GetFuncNameList()[methodName] + } + return + +} + +// CheckFuncName 检查方法名是否存在 +func (tk Tasks) CheckFuncName(funcName string) (exists bool) { + funcList := tk.taskJob.GetFuncNameList() + _, exists = funcList[funcName] + return +} + +// ParseParameters 解析参数 +func (tk Tasks) ParseParameters(parseData string) (params []interface{}, err error) { + parts := strings.Split(parseData, "|") + params = make([]interface{}, len(parts)) + for i, part := range parts { + params[i] = part + } + return +} + +func NewTasks() (tk *Tasks, err error) { + defer func() { + e := recover() + if e != nil { + err = errors.Errorf("%v", e) + } + }() + w := New( + WithHandler(func(ctx context.Context, p Payload) error { + return process(task{ + ctx: ctx, + payload: p, + }) + }), + ) + if w.Error != nil { + err = errors.WithMessage(w.Error, "initialize worker failed") + return + } + + tk = &Tasks{ + worker: w, + } + g.Log().Debug(context.Background(), "initialize worker success") + return +} + +type task struct { + ctx context.Context + payload Payload + job tasks.TaskJob +} + +// process 处理任务 +func process(t task) (err error) { + tr := otel.Tracer("task") + _, span := tr.Start(t.ctx, "Task") + defer span.End() + var taskData tasks.TaskJob + err = json.Unmarshal(t.payload.Payload, &taskData) + if err != nil { + return err + } + + err = CallMethod(&taskData) + if err != nil { + fmt.Println("CallMethod err:", err.Error()) + return err + } + return +} + +// CallMethod 调用任务的实际方法 +func CallMethod(task *tasks.TaskJob) (err error) { + // 判断task.Params是否为nil + if task.Params == nil { + task.Params = []interface{}{} + } else { + var paramsData []interface{} + // 判断task.Params内的值是否为基本类型 + for _, param := range task.Params { + switch reflect.TypeOf(param).Kind() { + case reflect.Float64: + // 如果是数字 ,检查是否可以转换为整数 + v := param.(float64) + if v == float64(int64(v)) { + // 如果可以转换为整数,则转换为int类型 + paramsData = append(paramsData, int64(v)) + } else { + // 如果不能转换为整数,则保留为float64类型 + paramsData = append(paramsData, v) + } + case reflect.String, reflect.Bool: + // 对于字符串、布尔和空值,直接添加到Params中 + paramsData = append(paramsData, param) + // 可以添加更多的类型处理 + default: + panic("unhandled default case") + } + } + task.Params = paramsData + } + + // 获取TaskModel的反射值 + taskValue := reflect.ValueOf(task) + // 准备要调用的方法的参数 + var args []reflect.Value + for _, param := range task.Params { + if !reflect.ValueOf(param).IsValid() { + err = errors.New("invalid parameter") + return + } + args = append(args, reflect.ValueOf(param)) + } + // 查找并执行方法 + method := taskValue.MethodByName(task.MethodName) + if method.IsValid() { + if method.Type().NumIn() == 0 { + method.Call(nil) + return + } else if method.Type().NumIn() != len(args) { + err = errors.New("incorrect number of parameters") + return + } + g.Log().Debug(context.Background(), "执行的任务:", task.MethodName) + if len(task.Params) > 0 { + method.Call(args) + } else { + method.Call(nil) + } + } else { + errInfo := fmt.Sprintf("Method not found: %s", task.MethodName) + err = errors.New(errInfo) + } + return +} + +// UnmarshalTask 解析TaskJob,使用自定义JSON解析来处理类型问题 +func UnmarshalTask(data []byte) (*tasks.TaskJob, error) { + var task tasks.TaskJob + dec := json.NewDecoder(bytes.NewReader(data)) + // 使用json.Decoder逐个解析Token,自定义解析逻辑 + for { + t, err := dec.Token() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + // 根据Token类型处理 + switch v := t.(type) { + case float64: + // 如果是数字 ,检查是否可以转换为整数 + if v == float64(int64(v)) { + // 如果可以转换为整数,则转换为int类型 + task.Params = append(task.Params, int64(v)) + } else { + // 如果不能转换为整数,则保留为float64类型 + task.Params = append(task.Params, v) + } + case string, bool, nil: + // 对于字符串、布尔和空值,直接添加到Params中 + task.Params = append(task.Params, v) + // 可以添加更多的类型处理 + } + } + return &task, nil +} diff --git a/pkg/worker/work_proces.go b/pkg/worker/work_proces.go new file mode 100644 index 0000000..dd124ab --- /dev/null +++ b/pkg/worker/work_proces.go @@ -0,0 +1,65 @@ +package worker + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/guid" +) + +// Scheduled 任务调度器 +type Scheduled struct { + topic string + w *Worker +} + +// Process 任务具体处理过程接口,实现该接口即可加入到任务队列中 +type Process interface { + GetTopic() string // 获取消费主题 + Handle(ctx context.Context, p Payload) (err error) // 处理过程的方法 +} + +func RegisterProcess(p Process) (s *Scheduled) { + s = &Scheduled{} + topic := p.GetTopic() + if topic != "" { + s.w = New( + WithGroup(topic), + WithHandler(p.Handle), + ) + } else { + s.w = New( + WithHandler(p.Handle), + ) + } + return +} + +// Push 采用消息队列的方式执行任务 +func (s *Scheduled) Push(ctx context.Context, topic string, data []byte, timeout int) (err error) { + err = s.w.Once( + WithRunUuid(guid.S()), + WithRunPayload(data), //传递参数 + WithRunGroup(topic), + //task.WithRunAt(time.Now().Add(time.Duration(10)*time.Second)), //延迟5秒执行 + WithRunTimeout(timeout), //超时时间10秒 + ) + if err != nil { + g.Log().Debug(ctx, "Run Queue TaskWorker %s Error: %v", topic, err) + } + return +} + +// Cron 采用定时任务的方式执行任务 +func (s *Scheduled) Cron(ctx context.Context, topic, cronExpr string, data []byte) (err error) { + s.topic = topic + err = s.w.Cron( + WithRunUuid(topic), + WithRunGroup(topic), + WithRunExpr(cronExpr), + WithRunPayload(data), //传递参数 + ) + if err != nil { + g.Log().Debug(ctx, "Run Cron TaskWorker %s Error: %v", topic, err) + } + return +} diff --git a/pkg/worker/worker.go b/pkg/worker/worker.go new file mode 100644 index 0000000..ac8daf2 --- /dev/null +++ b/pkg/worker/worker.go @@ -0,0 +1,443 @@ +package worker + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/util/guid" + "github.com/golang-module/carbon/v2" + "github.com/gorhill/cronexpr" + "github.com/hibiken/asynq" + "github.com/redis/go-redis/v9" + "io" + "net/http" + "sagooiot/pkg/utility/nx" + "strings" + "time" +) + +type Worker struct { + ops Options + redis redis.UniversalClient + redisOpt asynq.RedisConnOpt + lock *nx.Nx + client *asynq.Client + inspector *asynq.Inspector + Error error +} + +type periodTask struct { + Expr string `json:"expr"` // cron expr github.com/robfig/cron/v3 + Group string `json:"group"` + Uid string `json:"uid"` + Payload []byte `json:"payload"` + Next int64 `json:"next"` // next schedule unix timestamp + Processed int64 `json:"processed"` // run times + MaxRetry int `json:"maxRetry"` + Timeout int `json:"timeout"` +} + +func (p *periodTask) String() (str string) { + bs, _ := json.Marshal(p) + str = string(bs) + return +} + +func (p *periodTask) FromString(str string) { + err := json.Unmarshal([]byte(str), p) + if err != nil { + return + } + return +} + +type periodTaskHandler struct { + tk Worker +} + +type Payload struct { + Group string `json:"group"` + Uid string `json:"uid"` + Payload []byte `json:"payload"` +} + +func (p Payload) String() (str string) { + bs, _ := json.Marshal(p) + str = string(bs) + return +} + +func (p periodTaskHandler) ProcessTask(ctx context.Context, t *asynq.Task) (err error) { + uid := guid.S() + group := strings.TrimSuffix(strings.TrimSuffix(t.Type(), ".once"), ".cron") + payload := Payload{ + Group: group, + Uid: t.ResultWriter().TaskID(), + Payload: t.Payload(), + } + defer func() { + if err != nil { + glog.Debugf(ctx, "run task failed. uuid: %s task: %s Error:%s", uid, payload, err) + } + }() + if p.tk.ops.handler != nil { + err = p.tk.ops.handler(ctx, payload) + } else if p.tk.ops.handlerNeedWorker != nil { + err = p.tk.ops.handlerNeedWorker(p.tk, ctx, payload) + } else if p.tk.ops.callback != "" { + err = p.httpCallback(ctx, payload) + } else { + glog.Debugf(ctx, "no task handler. uuid: %s task: %s Error:%s", uid, payload, err) + } + // save processed count + p.tk.processed(ctx, payload.Uid) + return +} + +func (p periodTaskHandler) httpCallback(ctx context.Context, payload Payload) (err error) { + client := &http.Client{} + body := payload.String() + var r *http.Request + r, _ = http.NewRequestWithContext(ctx, http.MethodPost, p.tk.ops.callback, bytes.NewReader([]byte(body))) + r.Header.Add("Content-Type", "application/json") + var res *http.Response + res, err = client.Do(r) + if err != nil { + return + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + glog.Error(ctx, err) + } + }(res.Body) + if res.StatusCode != http.StatusOK { + err = ErrHttpCallbackInvalidStatusCode + } + return +} + +// New 创建一个新的任务处理器 +func New(options ...func(*Options)) (tk *Worker) { + ops := getOptionsOrSetDefault(nil) + for _, f := range options { + f(ops) + } + tk = &Worker{} + if ops.redisUri == "" { + tk.Error = errors.Unwrap(ErrRedisNil) + return + } + rs, err := asynq.ParseRedisURI(ops.redisUri) + if err != nil { + tk.Error = errors.Unwrap(ErrRedisInvalid) + return + } + // 将组前缀添加到拆分的差异组 + ops.redisPeriodKey = strings.Join([]string{"ops.group", ops.redisPeriodKey}, ".") + rd := rs.MakeRedisClient().(redis.UniversalClient) + client := asynq.NewClient(rs) + inspector := asynq.NewInspector(rs) + // initialize redis lock + nxLock := nx.New( + nx.WithRedis(rd), + nx.WithExpire(10), + nx.WithKey(strings.Join([]string{ops.redisPeriodKey, "lock"}, ".")), + ) + // initialize server + srv := asynq.NewServer( + rs, + asynq.Config{ + Concurrency: 10, // 最大同时执行任务数 + Queues: map[string]int{ + ops.group: 10, + }, + LogLevel: 4, + }, + ) + go func() { + var h periodTaskHandler + h.tk = *tk + if e := srv.Run(h); e != nil { + glog.Error(context.Background(), "run task handler failed") + } + }() + tk.ops = *ops + tk.redis = rd + tk.redisOpt = rs + tk.lock = nxLock + tk.client = client + tk.inspector = inspector + // initialize scanner + go func() { + for { + time.Sleep(time.Second) + tk.scan() + } + }() + if tk.ops.clearArchived > 0 { + // initialize clear archived + go func() { + for { + time.Sleep(time.Duration(tk.ops.clearArchived) * time.Second) + tk.clearArchived() + } + }() + } + return +} + +func (wk Worker) Once(options ...func(*RunOptions)) (err error) { + ops := getRunOptionsOrSetDefault(nil) + for _, f := range options { + f(ops) + } + if ops.uid == "" { + err = errors.Unwrap(ErrUuidNil) + return + } + t := asynq.NewTask(strings.Join([]string{ops.group, "once"}, "."), ops.payload, asynq.TaskID(ops.uid)) + taskOpts := []asynq.Option{ + asynq.Queue(wk.ops.group), + asynq.MaxRetry(wk.ops.maxRetry), + asynq.Timeout(time.Duration(ops.timeout) * time.Second), + } + if ops.maxRetry > 0 { + taskOpts = append(taskOpts, asynq.MaxRetry(ops.maxRetry)) + } + if ops.retention > 0 { + taskOpts = append(taskOpts, asynq.Retention(time.Duration(ops.retention)*time.Second)) + } else { + taskOpts = append(taskOpts, asynq.Retention(time.Duration(wk.ops.retention)*time.Second)) + } + if ops.in != nil { + taskOpts = append(taskOpts, asynq.ProcessIn(*ops.in)) + } else if ops.at != nil { + taskOpts = append(taskOpts, asynq.ProcessAt(*ops.at)) + } else if ops.now { + taskOpts = append(taskOpts, asynq.ProcessIn(time.Millisecond)) + } + _, err = wk.client.Enqueue(t, taskOpts...) + if ops.replace && errors.Is(err, asynq.ErrTaskIDConflict) { + // remove old one if replace = true + ctx := wk.getDefaultTimeoutCtx() + if ops.ctx != nil { + ctx = ops.ctx + } + err = wk.Remove(ctx, ops.uid) + if err != nil { + return + } + _, err = wk.client.Enqueue(t, taskOpts...) + } + return +} + +// Cron 设置周期性任务 +func (wk Worker) Cron(options ...func(*RunOptions)) (err error) { + ops := getRunOptionsOrSetDefault(nil) + for _, f := range options { + f(ops) + } + if ops.uid == "" { + err = errors.Unwrap(ErrUuidNil) + return + } + var next int64 + next, err = getNext(ops.expr, 0) + if err != nil { + err = errors.Unwrap(ErrExprInvalid) + return + } + t := periodTask{ + Expr: ops.expr, + Group: strings.Join([]string{ops.group, "cron"}, "."), + Uid: ops.uid, + Payload: ops.payload, + Next: next, + MaxRetry: ops.maxRetry, + Timeout: ops.timeout, + } + ctx := wk.getDefaultTimeoutCtx() + res, err := wk.redis.HGet(ctx, wk.ops.redisPeriodKey, ops.uid).Result() + if err == nil { + var oldT periodTask + err = json.Unmarshal([]byte(res), &oldT) + if err != nil { + return err + } + if oldT.Expr != t.Expr { + // 删除旧任务 + err = wk.Remove(ctx, t.Uid) + if err != nil { + return err + } + } + } + _, err = wk.redis.HSet(ctx, wk.ops.redisPeriodKey, ops.uid, t.String()).Result() + if err != nil { + err = errors.Unwrap(ErrSaveCron) + return + } + return +} + +// Remove 移除任务 +func (wk Worker) Remove(ctx context.Context, uid string) (err error) { + err = wk.lock.MustLock(ctx) + if err != nil { + return + } + defer wk.lock.Unlock(ctx) + wk.redis.HDel(ctx, wk.ops.redisPeriodKey, uid) + + err = wk.inspector.DeleteTask(wk.ops.group, uid) + return +} + +func (wk Worker) processed(ctx context.Context, uid string) { + err := wk.lock.MustLock(ctx) + if err != nil { + return + } + defer wk.lock.Unlock(ctx) + t, e := wk.redis.HGet(ctx, wk.ops.redisPeriodKey, uid).Result() + if e == nil || !errors.Is(e, redis.Nil) { + var item periodTask + item.FromString(t) + item.Processed++ + wk.redis.HSet(ctx, wk.ops.redisPeriodKey, uid, item.String()) + } + return +} + +// scan 扫描并处理任务队列 +func (wk Worker) scan() { + ctx := wk.getDefaultTimeoutCtx() + ok := wk.lock.Lock() + if !ok { + return + } + defer wk.lock.Unlock() + m, _ := wk.redis.HGetAll(ctx, wk.ops.redisPeriodKey).Result() + p := wk.redis.Pipeline() + ops := wk.ops + if ops.group == "task" { + for _, v := range m { + var item periodTask + item.FromString(v) + next, _ := getNext(item.Expr, item.Next) + t := asynq.NewTask(item.Group, item.Payload, asynq.TaskID(item.Uid)) + taskOpts := []asynq.Option{ + asynq.Queue(ops.group), + asynq.MaxRetry(ops.maxRetry), + asynq.Timeout(time.Duration(item.Timeout) * time.Second), + } + if item.MaxRetry > 0 { + taskOpts = append(taskOpts, asynq.MaxRetry(item.MaxRetry)) + } + diff := next - item.Next + if diff > 10 { + retention := diff / 3 + if diff > 600 { + // 最大保留时间为 10 分钟 + retention = 600 + } + // 设置保留时间以避免在短时间内重复任务 + taskOpts = append(taskOpts, asynq.Retention(time.Duration(retention)*time.Second)) + } + taskOpts = append(taskOpts, asynq.ProcessAt(time.Unix(item.Next, 0))) + _, err := wk.client.Enqueue(t, taskOpts...) + // 入队成功,更新下一个 + if err == nil { + item.Next = next + p.HSet(ctx, wk.ops.redisPeriodKey, item.Uid, item.String()) + } + } + // 批量保存到缓存 + _, err := p.Exec(ctx) + if err != nil { + return + } + } + + return +} + +// clearArchived 清除已归档的任务 +func (wk Worker) clearArchived() { + list, err := wk.inspector.ListArchivedTasks(wk.ops.group, asynq.Page(1), asynq.PageSize(100)) + if err != nil { + return + } + ctx := wk.getDefaultTimeoutCtx() + for _, item := range list { + last := carbon.CreateFromStdTime(item.LastFailedAt) + if !last.IsZero() && item.Retried < item.MaxRetry { + continue + } + uid := item.ID + var flag bool + if strings.HasSuffix(item.Type, ".cron") { + // cron task + t, e := wk.redis.HGet(ctx, wk.ops.redisPeriodKey, uid).Result() + if e == nil || !errors.Is(e, redis.Nil) { + var task periodTask + task.FromString(t) + next, _ := getNext(task.Expr, task.Next) + diff := next - task.Next + if diff <= 60 { + if carbon.Now().Gt(last.AddMinutes(5)) { + flag = true + } + } else if diff <= 600 { + if carbon.Now().Gt(last.AddMinutes(30)) { + flag = true + } + } else if diff <= 3600 { + if carbon.Now().Gt(last.AddHours(2)) { + flag = true + } + } else { + if carbon.Now().Gt(last.AddHours(5)) { + flag = true + } + } + } + } else { + // once task, has failed for more than 5 minutes + if carbon.Now().Gt(last.AddMinutes(5)) { + flag = true + } + } + if flag { + err := wk.inspector.DeleteTask(wk.ops.group, uid) + if err != nil { + return + } + } + } +} + +// getDefaultTimeoutCtx 获取带有默认超时的上下文 +func (wk Worker) getDefaultTimeoutCtx() context.Context { + c, _ := context.WithTimeout(context.Background(), time.Duration(wk.ops.timeout)*time.Second) + return c +} + +// getNext 计算下一次执行时间 +func getNext(expr string, timestamp int64) (next int64, err error) { + var e *cronexpr.Expression + e, err = cronexpr.Parse(expr) + if err != nil { + return + } + t := carbon.Now().ToStdTime() + if timestamp > 0 { + t = carbon.CreateFromTimestamp(timestamp).ToStdTime() + } + next = e.Next(t).Unix() + return +} diff --git a/pkg/worker/worker_test.go b/pkg/worker/worker_test.go new file mode 100644 index 0000000..bb17972 --- /dev/null +++ b/pkg/worker/worker_test.go @@ -0,0 +1,38 @@ +package worker + +import ( + "context" + "fmt" + "testing" + "time" +) + +func TestWorker(t *testing.T) { + // cron worker + wk1 := New( + WithRedisUri("redis://127.0.0.1:6379/0"), + WithHandler(handleProcess), + ) + // example 2: run at every 8 minute + err := wk1.Cron( + WithRunUuid("order2"), + WithRunGroup("task2"), + WithRunExpr("0/1 * * * ?"), + ) + + fmt.Println(err) + + time.Sleep(time.Hour) +} +func handleProcess(ctx context.Context, p Payload) (err error) { + fmt.Println(ctx, "=====", p.Uid, p.Group) + switch p.Group { + case "task1": + + fmt.Println("=====", p.Uid, p.Group) + case "task2": + fmt.Println(p.Uid, p.Group) + + } + return +} diff --git a/plugin_test.go b/plugin_test.go index d2388a7..a6397b3 100644 --- a/plugin_test.go +++ b/plugin_test.go @@ -3,15 +3,15 @@ package main import ( "fmt" "github.com/gogf/gf/v2/util/gconv" - "github.com/sagoo-cloud/sagooiot/extend" - "github.com/sagoo-cloud/sagooiot/extend/model" - "github.com/sagoo-cloud/sagooiot/extend/module" "net" + "sagooiot/pkg/plugins" + "sagooiot/pkg/plugins/model" + "sagooiot/pkg/plugins/module" "testing" ) func TestManagerInit(t *testing.T) { - manager := extend.NewManager("protocol", "protocol-*", "./plugins/built", &module.ProtocolPlugin{}) + manager := plugins.NewManager("protocol", "protocol-*", "./plugins/built", &module.ProtocolPlugin{}) defer manager.Dispose() err := manager.Init() if err != nil { @@ -23,13 +23,14 @@ func TestManagerInit(t *testing.T) { t.Log(info.Path) t.Log(info.Client) } + t.Log(manager) } // 测试获取插件信息 func TestProtocolInfo(t *testing.T) { - p, err := extend.GetProtocolPlugin().GetProtocolPlugin("tgn52") + p, err := plugins.GetProtocolPlugin().GetProtocolByName("tgn52") if err != nil { return } @@ -46,14 +47,17 @@ type TestData struct { // 测试协议的编码方法 func TestProtocolEncode(t *testing.T) { - p, err := extend.GetProtocolPlugin().GetProtocolPlugin("tgn52") + p, err := plugins.GetProtocolPlugin().GetProtocolByName("tf100") if err != nil { t.Fatal(err.Error()) } td := new(TestData) td.Name = "aaaa" td.Value = "bbbbb" - res := p.Encode(td) + + var reqData = model.DataReq{} + reqData.Data = gconv.Bytes(td) + res := p.Encode(reqData) if err != nil { t.Fatal(err.Error()) } @@ -63,7 +67,7 @@ func TestProtocolEncode(t *testing.T) { // 测试自定义协议解析 func TestProtocol(t *testing.T) { data := gconv.Bytes("NB1;1234567;1;2;+25.5;00;030;+21;+22") - p, err := extend.GetProtocolPlugin().GetProtocolPlugin("tgn52") + p, err := plugins.GetProtocolPlugin().GetProtocolByName("tgn52") if err != nil { return } @@ -78,7 +82,7 @@ func TestProtocol(t *testing.T) { // 测试插件服务使用,需要先将要测试的插件进行编译 func TestProtocolPluginServer(t *testing.T) { - extend.GetProtocolPlugin() + plugins.GetProtocolPlugin() NetData() } @@ -115,7 +119,7 @@ func doServerStuff(conn net.Conn) { fmt.Printf("Received data: %v\n", string(buf[:l])) //获取协议插件解析后的数据 传入插件ID,及需要解析的数据 - data, err := extend.GetProtocolPlugin().GetProtocolUnpackData("tgn52", buf[:l]) + data, err := plugins.GetProtocolPlugin().GetProtocolDecodeData("tgn52", buf[:l]) fmt.Println("============通过插件获取数据:", data) } } @@ -123,30 +127,65 @@ func doServerStuff(conn net.Conn) { func TestNotice(t *testing.T) { // 准备通知数据 + var nso []model.NoticeSendObject + nso = append(nso, model.NoticeSendObject{ + Name: "mail", + Value: "xjy@sagoo.cn", + }) + nso = append(nso, model.NoticeSendObject{ + Name: "wework", + Value: "all", + }) + var msg = model.NoticeInfoData{} - msg.Totag = "[{\"name\":\"mail\",\"value\":\"940290@qq.com\"},{\"name\":\"webhook\",\"value\":\"cccc\"}],{\"name\":\"sms\",\"value\":\"13700005102\"}]" - msg.MsgBody = "new111111" + msg.Totag = nso + msg.MsgBody = "{'code':'19001'}" msg.MsgTitle = "title111112222" + msg.TemplateCode = "SMS_464050874" //通过邮件发送通知 - res, err := extend.GetNoticePlugin().NoticeSend("mail", msg) + res, err := plugins.GetNoticePlugin().NoticeSend("mail", msg) if err != nil { - t.Fatal(err.Error()) + t.Log("Error: ", err.Error()) } t.Log(res) + // + ////通过短信发送通知 + //res, err := plugins.GetNoticePlugin().NoticeSend("sms", msg) + //if err != nil { + // t.Log("Error: ", err.Error()) + //} + //t.Log(res) + // + //通过webhook发送通知 + //res, err := plugins.GetNoticePlugin().NoticeSend("webhook", msg) + //if err != nil { + // t.Log("Error: ", err.Error()) + //} + //t.Log(res) + // + ////通过企业微信发送通知 + //res, err := plugins.GetNoticePlugin().NoticeSend("wework", msg) + //if err != nil { + // t.Log("Error: ", err.Error()) + //} + //t.Log(res) + // + ////通过钉钉发送通知 + //res, err = plugins.GetNoticePlugin().NoticeSend("dingding", msg) + //if err != nil { + // t.Log("Error: ", err.Error()) + //} + //t.Log(res) - //通过企业微信发送通知 - res, err = extend.GetNoticePlugin().NoticeSend("wework", msg) - if err != nil { - t.Fatal(err.Error()) - } - t.Log(res) +} - //通过钉钉发送通知 - res, err = extend.GetNoticePlugin().NoticeSend("dingding", msg) +// TestModbus 测试modbus +func TestModbus(t *testing.T) { + data := gconv.Bytes("aadsfsfsfdfsfsdfsfs") + res, err := plugins.GetProtocolPlugin().GetProtocolDecodeData("modbus", data) if err != nil { t.Fatal(err.Error()) } t.Log(res) - } diff --git a/plugins/Makefile b/plugins/Makefile deleted file mode 100644 index 7f4a378..0000000 --- a/plugins/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -PROTOCOL_PLUGIN_DIRS=$(wildcard ./protocol/*) #网络协议解析插件目录 -NOTICE_PLUGIN_DIRS=$(wildcard ./notice/*) #通知服务插件目录 - -BUILD_SYS=local #编译方式: local,本地环境编译;linux,linux交叉编译 - -all: build-plugins - -clean: clean-plugins - -build-plugins: $(PROTOCOL_PLUGIN_DIRS) $(NOTICE_PLUGIN_DIRS) - -clean-plugins: - rm -f ./built/* - -$(PROTOCOL_PLUGIN_DIRS): - $(info Clubber plugins at: $(PROTOCOL_PLUGIN_DIRS)) - $(MAKE) $(BUILD_SYS) -C $@ - -$(NOTICE_PLUGIN_DIRS): - $(info Clubber plugins at: $(NOTICE_PLUGIN_DIRS)) - $(MAKE) $(BUILD_SYS) -C $@ - -.PHONY: all $(PROTOCOL_PLUGIN_DIRS) $(NOTICE_PLUGIN_DIRS) \ No newline at end of file diff --git a/plugins/notice/dingding/Makefile b/plugins/notice/dingding/Makefile deleted file mode 100644 index 06580d1..0000000 --- a/plugins/notice/dingding/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -BINARY_NAME=notice-dingding - -local: - echo "========local============" - go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built - -linux: - echo "========linux============" - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built \ No newline at end of file diff --git a/plugins/notice/dingding/dingding.go b/plugins/notice/dingding/dingding.go deleted file mode 100644 index f852e0e..0000000 --- a/plugins/notice/dingding/dingding.go +++ /dev/null @@ -1,88 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gogf/gf/v2/util/gconv" - gplugin "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginType" - "github.com/sagoo-cloud/sagooiot/extend/model" - extend "github.com/sagoo-cloud/sagooiot/extend/module" - "github.com/sagoo-cloud/sagooiot/extend/sdk" - "github.com/sagoo-cloud/sagooiot/plugins/notice/dingding/internal" - "net/rpc" -) - -type Options struct { - PayloadURL string - Secret string - Subject string - Body string -} - -// NoticeDingding 实现 -type NoticeDingding struct{} - -func (NoticeDingding) Info() model.PluginInfo { - var res = model.PluginInfo{} - res.Types = PluginType.Notice - res.Name = "dingding" - res.Title = "Ding Ding" - res.Author = "Microrain" - res.Description = "通过钉钉方式发送通知" - res.Version = "0.01" - return res -} - -func (NoticeDingding) Send(data []byte) (res model.JsonRes) { - //解析通知数据 - nd, err := sdk.DecodeNoticeData(data) - if err != nil { - res.Code = 2 - res.Message = "插件数据解析失败" - res.Data = err.Error() - return res - } - - // 设定相关参数 - opts := []internal.Option{ - internal.AppKey(gconv.String(nd.Config["AppKey"])), - internal.AppSecret(gconv.String(nd.Config["AppSecret"])), - internal.AgentID(gconv.String(nd.Config["AgentID"])), - } - ding := internal.GetDingdingChannel(opts...) - accessToken, err := ding.GetAccessToken() - if err != nil { - fmt.Println(err) - return - } - err = ding.SendTextMessage(accessToken, "", "", "", "Hello, world!", "") - if err != nil { - fmt.Println(err) - return - } - return -} - -// DingdingPlugin 插件接口实现 -type DingdingPlugin struct{} - -// Server 此方法由插件进程延迟调 -func (DingdingPlugin) Server(*gplugin.MuxBroker) (interface{}, error) { - return &extend.NoticeRPCServer{Impl: new(NoticeDingding)}, nil -} - -// Client 此方法由宿主进程调用 -func (DingdingPlugin) Client(b *gplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &extend.NoticeRPC{Client: c}, nil -} - -func main() { - gplugin.Serve(&gplugin.ServeConfig{ - HandshakeConfig: extend.HandshakeConfig, - Plugins: pluginMap, - }) -} - -var pluginMap = map[string]gplugin.Plugin{ - "dingding": new(DingdingPlugin), -} diff --git a/plugins/notice/dingding/internal/options.go b/plugins/notice/dingding/internal/options.go deleted file mode 100644 index 9d3c9d9..0000000 --- a/plugins/notice/dingding/internal/options.go +++ /dev/null @@ -1,26 +0,0 @@ -package internal - -type options struct { - appKey string - appSecret string - agentID string -} - -type Option func(c *options) - -func AppKey(d string) Option { - return func(opts *options) { - opts.appKey = d - } -} - -func AppSecret(d string) Option { - return func(opts *options) { - opts.appSecret = d - } -} -func AgentID(d string) Option { - return func(opts *options) { - opts.agentID = d - } -} diff --git a/plugins/notice/dingding/internal/send.go b/plugins/notice/dingding/internal/send.go deleted file mode 100644 index fe328bc..0000000 --- a/plugins/notice/dingding/internal/send.go +++ /dev/null @@ -1,127 +0,0 @@ -package internal - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/os/gcache" - "github.com/sagoo-cloud/sagooiot/extend/model" - "io/ioutil" - "net/http" - "sync" - "time" -) - -type dingdingChannel struct { - opts *options -} - -var ins *dingdingChannel - -var once sync.Once - -//GetDingdingChannel 构造方法 -func GetDingdingChannel(opts ...Option) *dingdingChannel { - clusterOpts := options{} - for _, opt := range opts { - // 函数指针的赋值调用 - opt(&clusterOpts) - } - once.Do(func() { - ins = &dingdingChannel{} - }) - ins.opts = &clusterOpts - - return ins -} - -// GetAccessToken 获取 access_token -func (d *dingdingChannel) GetAccessToken() (accessToken string, err error) { - - cacheKey := "Dingding" + d.opts.agentID - //存缓存里获取accessToken - accessTokenData, _ := gcache.Get(context.TODO(), cacheKey) - if accessTokenData != nil { - accessToken = accessTokenData.String() - return - } - - url := fmt.Sprintf("https://oapi.dingtalk.com/gettoken?appkey=%s&appsecret=%s", d.opts.appKey, d.opts.appSecret) - resp, err := http.Get(url) - if err != nil { - return "", err - } - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - - var data struct { - Errcode int `json:"errcode"` - Errmsg string `json:"errmsg"` - Token string `json:"access_token"` - } - if err := json.Unmarshal(body, &data); err != nil { - return "", err - } - if data.Errcode != 0 { - return "", fmt.Errorf("%d: %s", data.Errcode, data.Errmsg) - } - - // 钉钉的AccessToken的有效期是2小时,这里设置为缓存1小时 - _, err = gcache.SetIfNotExist(context.TODO(), cacheKey, data.Token, time.Hour) - - return -} - -//Send 发送 -func (d *dingdingChannel) Send(accessToken string, msg model.NoticeInfoData) (err error) { - var sendObjectList []model.NoticeSendObject - err = gjson.DecodeTo(msg.Totag, &sendObjectList) - if err != nil { - g.Log().Error(context.TODO(), err) - return - } - - var touser string - for _, object := range sendObjectList { - if object.Name == "dingding" { - touser = object.Value + "," - } - } - err = d.SendTextMessage(accessToken, touser, "", "", msg.MsgBody, "") - - return -} - -// SendTextMessage 发送文本消息 -func (d *dingdingChannel) SendTextMessage(accessToken, touser, totag, toparty, message, atMobiles string) error { - url := fmt.Sprintf("https://oapi.dingtalk.com/message/send?access_token=%s", accessToken) - - data := map[string]interface{}{ - "touser": touser, - "toparty": toparty, - "totag": totag, - "msgtype": "text", - "agentid": d.opts.agentID, - "text": map[string]string{ - "content": message, - }, - "at": map[string]interface{}{ - "atMobiles": []string{atMobiles}, - "isAtAll": false, - }, - } - payload, err := json.Marshal(data) - if err != nil { - return err - } - - _, err = http.Post(url, "application/json", bytes.NewReader(payload)) - return err -} diff --git a/plugins/notice/dingding/readme.MD b/plugins/notice/dingding/readme.MD deleted file mode 100644 index bc3eb33..0000000 --- a/plugins/notice/dingding/readme.MD +++ /dev/null @@ -1,27 +0,0 @@ -首先,你需要在钉钉开发平台中创建一个应用并获取到对应的 AppKey 和 AppSecret。然后,你可以使用以下步骤来发送通知: - -使用 AppKey 和 AppSecret 获取到 access_token。你可以使用以下 API 获取 access_token: -``` -https://oapi.dingtalk.com/gettoken?appkey=APPKEY&appsecret=APPSECRET - -``` -使用 access_token 和其他所需的参数调用钉钉的发送消息 API 发送通知。你可以使用以下 API 发送通知: - -``` -https://oapi.dingtalk.com/message/send?access_token=ACCESS_TOKEN -``` -具体的,你需要构造一个 JSON 对象作为请求体,包含以下字段: - -touser: 接收通知的用户的 userid,多个用户用逗号分隔。 - -toparty: 接收通知的部门的 ID,多个部门用逗号分隔。 - -totag: 接收通知的标签的 ID,多个标签用逗号分隔。 - -msgtype: 消息类型,此处应为 "text"。 - -agentid: 发送通知的应用的 ID。 - -text: 消息内容,类型为文本时,此字段包含文本消息的内容。 - -at: 被 @ 的用户的 userid 列表,多个用户用逗号分隔。 \ No newline at end of file diff --git a/plugins/notice/mail/Makefile b/plugins/notice/mail/Makefile deleted file mode 100644 index 3dcbece..0000000 --- a/plugins/notice/mail/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -BINARY_NAME=notice-mail - -local: - echo "========local============" - go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built - -linux: - echo "========linux============" - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built \ No newline at end of file diff --git a/plugins/notice/mail/internal/options.go b/plugins/notice/mail/internal/options.go deleted file mode 100644 index ebf8616..0000000 --- a/plugins/notice/mail/internal/options.go +++ /dev/null @@ -1,33 +0,0 @@ -package internal - -type options struct { - mailHost string - mailPort int - mailUser string // 发件人 - mailPass string // 发件人密码 -} - -type Option func(c *options) - -func MailHost(d string) Option { - return func(opts *options) { - opts.mailHost = d - } -} - -func MailPort(d int) Option { - return func(opts *options) { - opts.mailPort = d - } -} -func MailUser(d string) Option { - return func(opts *options) { - opts.mailUser = d - } -} - -func MailPass(d string) Option { - return func(opts *options) { - opts.mailPass = d - } -} diff --git a/plugins/notice/mail/internal/send.go b/plugins/notice/mail/internal/send.go deleted file mode 100644 index bd1dee2..0000000 --- a/plugins/notice/mail/internal/send.go +++ /dev/null @@ -1,75 +0,0 @@ -package internal - -import ( - "context" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/extend/model" - "gopkg.in/gomail.v2" - "strings" - "sync" -) - -type mailChannel struct { - opts *options -} - -var ins *mailChannel - -var once sync.Once - -//GetMailChannel 构造方法 -func GetMailChannel(opts ...Option) *mailChannel { - clusterOpts := options{} - for _, opt := range opts { - // 函数指针的赋值调用 - opt(&clusterOpts) - } - once.Do(func() { - ins = &mailChannel{} - }) - ins.opts = &clusterOpts - - return ins -} - -//Send 发送 -func (m *mailChannel) Send(msg model.NoticeInfoData) (err error) { - - var sendObjectList []model.NoticeSendObject - err = gjson.DecodeTo(msg.Totag, &sendObjectList) - if err != nil { - g.Log().Error(context.TODO(), err) - return - } - - for _, object := range sendObjectList { - if object.Name == "mail" { - var data = make(map[string]string) - data["mailTo"] = object.Value - data["subject"] = msg.MsgTitle - data["body"] = msg.MsgBody - err = m.sendMail(data) - } - } - return err -} - -func (m *mailChannel) sendMail(data map[string]string) (err error) { - - mail := gomail.NewMessage() - //设置发件人 - mail.SetHeader("From", m.opts.mailUser) - //设置发送给多个用户 - mailArrTo := strings.Split(data["mailTo"], ",") - mail.SetHeader("To", mailArrTo...) - //设置邮件主题 - mail.SetHeader("Subject", data["subject"]) - - //设置邮件正文 - mail.SetBody("text/html", data["body"]) - d := gomail.NewDialer(m.opts.mailHost, m.opts.mailPort, m.opts.mailUser, m.opts.mailPass) - - err = d.DialAndSend(mail) - return err -} diff --git a/plugins/notice/mail/internal/send_test.go b/plugins/notice/mail/internal/send_test.go deleted file mode 100644 index f8d7cd4..0000000 --- a/plugins/notice/mail/internal/send_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package internal - -import ( - "fmt" - "github.com/sagoo-cloud/sagooiot/extend/model" - "testing" -) - -func TestSend(t *testing.T) { - var msg = model.NoticeInfoData{} - // 设定相关参数 - opts := []Option{ - MailHost("smtp.qq.com"), - MailPort(465), - MailUser("xinjy@qq.com"), - MailPass("zdqkqrdzplnabiig"), - } - m := GetMailChannel(opts...) - - msg.Totag = "[{\"name\":\"mail\",\"value\":\"940290@qq.com\"},{\"name\":\"webhook\",\"value\":\"cccc\"}]" - msg.MsgTitle = "test sagoo iot msg" - msg.MsgBody = "this is doc" - if err := m.Send(msg); err != nil { - fmt.Println(err) - } - -} diff --git a/plugins/notice/mail/mail.go b/plugins/notice/mail/mail.go deleted file mode 100644 index 4746398..0000000 --- a/plugins/notice/mail/mail.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/util/gconv" - gplugin "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginType" - "github.com/sagoo-cloud/sagooiot/extend/model" - extend "github.com/sagoo-cloud/sagooiot/extend/module" - "github.com/sagoo-cloud/sagooiot/extend/sdk" - "github.com/sagoo-cloud/sagooiot/plugins/notice/mail/internal" - "net/rpc" -) - -var logger *glog.Logger - -// NoticeMail 实现 -type NoticeMail struct{} - -func (NoticeMail) Info() model.PluginInfo { - var res = model.PluginInfo{} - res.Types = PluginType.Notice - res.Name = "mail" - res.Title = "电子邮件通知" - res.Author = "Microrain" - res.Description = "通过电子邮件发送通知" - res.Version = "0.01" - return res -} - -func (NoticeMail) Send(data []byte) (res model.JsonRes) { - - //解析通知数据 - nd, err := sdk.DecodeNoticeData(data) - if err != nil { - res.Code = 2 - res.Message = "邮件插件数据解析失败" - res.Data = err.Error() - return res - } - - // 设定相关参数 - opts := []internal.Option{ - internal.MailHost(gconv.String(nd.Config["MailHost"])), - internal.MailPort(gconv.Int(nd.Config["MailPort"])), - internal.MailUser(gconv.String(nd.Config["MailUser"])), - internal.MailPass(gconv.String(nd.Config["MailPass"])), - } - m := internal.GetMailChannel(opts...) - if err := m.Send(nd.Msg); err != nil { - res.Code = 2 - res.Message = "邮件发送失败" - res.Data = err.Error() - return res - } - - tmpData := gjson.New(nd) - res.Code = 0 - res.Message = "邮件发送成功" - res.Data = tmpData.MustToJsonString() - return res -} - -// MailPlugin 插件接口实现 -type MailPlugin struct{} - -// Server 此方法由插件进程延迟调 -func (MailPlugin) Server(*gplugin.MuxBroker) (interface{}, error) { - return &extend.NoticeRPCServer{Impl: new(NoticeMail)}, nil -} - -// Client 此方法由宿主进程调用 -func (MailPlugin) Client(b *gplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &extend.NoticeRPC{Client: c}, nil -} - -func main() { - gplugin.Serve(&gplugin.ServeConfig{ - HandshakeConfig: extend.HandshakeConfig, - Plugins: pluginMap, - }) -} - -var pluginMap = map[string]gplugin.Plugin{ - "mail": new(MailPlugin), -} diff --git a/plugins/notice/wework/Makefile b/plugins/notice/wework/Makefile deleted file mode 100644 index 933fd15..0000000 --- a/plugins/notice/wework/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -BINARY_NAME=notice-wework - -local: - echo "========local============" - go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built - -linux: - echo "========linux============" - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built \ No newline at end of file diff --git a/plugins/notice/wework/internal/internal.go b/plugins/notice/wework/internal/internal.go deleted file mode 100644 index 6268414..0000000 --- a/plugins/notice/wework/internal/internal.go +++ /dev/null @@ -1,73 +0,0 @@ -package internal - -import ( - "context" - "encoding/json" - "github.com/fastwego/wxwork/corporation" - "github.com/fastwego/wxwork/corporation/apis/message" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/errors/gerror" - "github.com/gogf/gf/v2/frame/g" - "github.com/sagoo-cloud/sagooiot/plugins/notice/wework/model" -) - -type Alarm struct { - Corp *corporation.Corporation - CorpApp *corporation.App - AppConfig corporation.AppConfig -} - -/** - * 建立私有变量 - */ -var instance *Alarm - -func GetInstance(corpid, agentID, secret, token, encodingAESKey string) *Alarm { - instance = new(Alarm) - // 加载应用的配置 - appConfigInfo, _ := g.Cfg().Get(context.TODO(), "wework.alarm", corporation.AppConfig{}) - p := gjson.New(appConfigInfo) - - if err := p.Scan(&instance.AppConfig); err != nil { - g.Log().Error(context.TODO(), err) - } - - instance.AppConfig.Token = token - instance.AppConfig.AgentId = agentID - instance.AppConfig.Secret = secret - instance.AppConfig.EncodingAESKey = encodingAESKey - - instance.Corp = corporation.New(corporation.Config{Corpid: corpid}) - instance.CorpApp = instance.Corp.NewApp(instance.AppConfig) - - return instance -} - -func (e *Alarm) SendMessage(toUser, content string) (interface{}, error) { - if content == "" { - return nil, gerror.New("发送的内容为空值") - } - - sendMsg := new(model.Text) - sendMsg.Agentid = e.AppConfig.AgentId - sendMsg.Touser = toUser //"@all" - sendMsg.Msgtype = "text" - sendMsg.Text.Content = content - sendMsg.DuplicateCheckInterval = 1800 - - jsonByte := ToJson(sendMsg) - - resp, err := message.Send(e.CorpApp, jsonByte) - if err != nil { - return nil, err - } - return resp, nil -} - -func ToJson(data interface{}) []byte { - jsonByte, err := json.Marshal(data) - if err != nil { - g.Log().Error(context.TODO(), err) - } - return jsonByte -} diff --git a/plugins/notice/wework/model/sendmessage.go b/plugins/notice/wework/model/sendmessage.go deleted file mode 100644 index bf16d7b..0000000 --- a/plugins/notice/wework/model/sendmessage.go +++ /dev/null @@ -1,32 +0,0 @@ -package model - -type Base struct { - Touser string `json:"touser"` - Toparty string `json:"toparty"` - Totag string `json:"totag"` - Msgtype string `json:"msgtype"` - Agentid string `json:"agentid"` - Safe int `json:"safe"` - EnableIDTrans int `json:"enable_id_trans"` - EnableDuplicateCheck int `json:"enable_duplicate_check"` - DuplicateCheckInterval int `json:"duplicate_check_interval"` -} - -//文本消息结构体 -type Text struct { - Base - Text struct { - Content string `json:"content"` - } `json:"text"` -} - -//文本卡片消息结构 -type Textcard struct { - Base - Textcard struct { - Title string `json:"title"` - Description string `json:"description"` - URL string `json:"url"` - Btntxt string `json:"btntxt"` - } `json:"textcard"` -} diff --git a/plugins/notice/wework/wework.go b/plugins/notice/wework/wework.go deleted file mode 100644 index d41c61d..0000000 --- a/plugins/notice/wework/wework.go +++ /dev/null @@ -1,99 +0,0 @@ -package main - -import ( - "context" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/util/gconv" - gplugin "github.com/hashicorp/go-plugin" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginType" - "github.com/sagoo-cloud/sagooiot/extend/model" - extend "github.com/sagoo-cloud/sagooiot/extend/module" - "github.com/sagoo-cloud/sagooiot/extend/sdk" - "github.com/sagoo-cloud/sagooiot/plugins/notice/wework/internal" - "net/rpc" -) - -type Options struct { - PayloadURL string - Secret string - Subject string - Body string -} - -// NoticeWework 实现 -type NoticeWework struct{} - -func (NoticeWework) Info() model.PluginInfo { - var res = model.PluginInfo{} - res.Types = PluginType.Notice - res.Name = "wework" - res.Title = "企业微信通知" - res.Author = "Microrain" - res.Description = "通过企业微信方式发送通知" - res.Version = "0.01" - return res -} - -func (NoticeWework) Send(data []byte) (res model.JsonRes) { - //解析通知数据 - nd, err := sdk.DecodeNoticeData(data) - if err != nil { - res.Code = 2 - res.Message = "插件数据解析失败" - res.Data = err.Error() - return res - } - - var sendObjectList []model.NoticeSendObject - err = gjson.DecodeTo(nd.Msg.Totag, &sendObjectList) - if err != nil { - g.Log().Error(context.TODO(), err) - return - } - corpid := gconv.String(nd.Config["Corpid"]) - agentID := gconv.String(nd.Config["AgentID"]) - secret := gconv.String(nd.Config["Secret"]) - token := gconv.String(nd.Config["Token"]) - encodingAESKey := gconv.String(nd.Config["EncodingAESKey"]) - - alarmService := internal.GetInstance(corpid, agentID, secret, token, encodingAESKey) - for _, object := range sendObjectList { - if object.Name == "wework" { - toUser := object.Value - content := nd.Msg.MsgBody - g.Log().Debug(context.TODO(), toUser, content) - data, err := alarmService.SendMessage(toUser, content) - if err != nil { - g.Log().Error(context.TODO(), err) - } - g.Log().Debug(context.TODO(), data) - } - } - - return -} - -// WeworkPlugin 插件接口实现 -type WeworkPlugin struct{} - -// Server 此方法由插件进程延迟调 -func (WeworkPlugin) Server(*gplugin.MuxBroker) (interface{}, error) { - return &extend.NoticeRPCServer{Impl: new(NoticeWework)}, nil -} - -// Client 此方法由宿主进程调用 -func (WeworkPlugin) Client(b *gplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &extend.NoticeRPC{Client: c}, nil -} - -func main() { - gplugin.Serve(&gplugin.ServeConfig{ - HandshakeConfig: extend.HandshakeConfig, - Plugins: pluginMap, - }) -} - -var pluginMap = map[string]gplugin.Plugin{ - "wework": new(WeworkPlugin), -} diff --git a/plugins/protocol/tgn52/Makefile b/plugins/protocol/tgn52/Makefile deleted file mode 100644 index 1b71b79..0000000 --- a/plugins/protocol/tgn52/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -BINARY_NAME=protocol-tgn52 - -local: - echo "========local============" - go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built - -linux: - echo "========linux============" - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ${BINARY_NAME} - mv ${BINARY_NAME} ../../built \ No newline at end of file diff --git a/plugins/protocol/tgn52/readme.md b/plugins/protocol/tgn52/readme.md deleted file mode 100644 index d2a2994..0000000 --- a/plugins/protocol/tgn52/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -# TG-N5 设备说明 - - -```go -NB1;1234567;1;2;+25.5;00;030;+21;+22 -``` - - -电量和信号都是123, 分别代表低中高 - -断点续传功能,说明之前有因如信号不稳定等因素,造成上传不成功的数据。缓存起来,有上传条件时,一起上传。先进先出原则:即先发送的温度数据为先存储的不成功数据,距离目前时间点较远。每个数据时间间隔为上传周期。上面例子中的就是30分钟 \ No newline at end of file diff --git a/plugins/protocol/tgn52/tgn52.go b/plugins/protocol/tgn52/tgn52.go deleted file mode 100644 index a68788b..0000000 --- a/plugins/protocol/tgn52/tgn52.go +++ /dev/null @@ -1,102 +0,0 @@ -package main - -import ( - "fmt" - "github.com/gogf/gf/v2/util/guid" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginHandleType" - "github.com/sagoo-cloud/sagooiot/extend/consts/PluginType" - "github.com/sagoo-cloud/sagooiot/extend/model" - "net/rpc" - "strings" - "time" - - gplugin "github.com/hashicorp/go-plugin" - plugin "github.com/sagoo-cloud/sagooiot/extend/module" -) - -// ProtocolTgn52 实现 -type ProtocolTgn52 struct{} - -func (p *ProtocolTgn52) Info() model.PluginInfo { - var res = model.PluginInfo{} - res.Name = "tgn52" - res.Types = PluginType.Notice - res.HandleType = PluginHandleType.TcpServer - res.Title = "TG-N5 v2设备协议" - res.Author = "Microrain" - res.Description = "对TG-N5插座设备进行数据采集v2" - res.Version = "0.01" - return res -} - -func (p *ProtocolTgn52) Encode(args interface{}) model.JsonRes { - var resp model.JsonRes - fmt.Println("接收到参数:", args) - return resp -} - -func (p *ProtocolTgn52) Decode(data model.DataReq) model.JsonRes { - var resp model.JsonRes - resp.Code = 0 - - tmpData := strings.Split(string(data.Data), ";") - var rd = make(map[string]model.Param) - - l := len(tmpData) - nowTime := time.Now().Unix() - if l > 7 { - rd["HeadStr"] = model.Param{Value: tmpData[0], Time: nowTime} - rd["DeviceID"] = model.Param{Value: tmpData[1], Time: nowTime} - rd["Signal"] = model.Param{Value: tmpData[2], Time: nowTime} - rd["Battery"] = model.Param{Value: tmpData[3], Time: nowTime} - rd["Temperature"] = model.Param{Value: tmpData[4], Time: nowTime} - rd["Humidity"] = model.Param{Value: tmpData[5], Time: nowTime} - rd["Cycle"] = model.Param{Value: tmpData[6], Time: nowTime} - //处理续传数据 - updateStr := make([]string, 0) - for i := 7; i < l; i++ { - updateStr = append(updateStr, tmpData[i]) - } - rd["Update"] = model.Param{Value: updateStr, Time: nowTime} - } - - resp.Code = 0 - resp.Data = model.SagooMqttModel{ - Id: guid.S(), - Version: "1.0", - Sys: model.SysInfo{Ack: 0}, - Params: rd, - Method: "thing.event.property.post", - ModelFuncName: "upProperty", - } - return resp -} - -// Tgn52Plugin 插件接口实现 -// 这有两种方法:服务器必须为此插件返回RPC服务器类型。我们为此构建了一个RPCServer。 -// 客户端必须返回我们的接口的实现通过RPC客户端。我们为此返回RPC。 -type Tgn52Plugin struct{} - -// Server 此方法由插件进程延迟调 -func (t *Tgn52Plugin) Server(*gplugin.MuxBroker) (interface{}, error) { - return &plugin.ProtocolRPCServer{Impl: new(ProtocolTgn52)}, nil -} - -// Client 此方法由宿主进程调用 -func (t *Tgn52Plugin) Client(b *gplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &plugin.ProtocolRPC{Client: c}, nil -} - -func main() { - //调用plugin.Serve()启动侦听,并提供服务 - //ServeConfig 握手配置,插件进程和宿主机进程,都需要保持一致 - gplugin.Serve(&gplugin.ServeConfig{ - HandshakeConfig: plugin.HandshakeConfig, - Plugins: pluginMap, - }) -} - -// 插件进程必须指定Impl,此处赋值为greeter对象 -var pluginMap = map[string]gplugin.Plugin{ - "tgn52": new(Tgn52Plugin), -} diff --git a/resource/public/.gitignore b/resource/public/.gitignore deleted file mode 100644 index 4ba2bdd..0000000 --- a/resource/public/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -greeter* -clubber* diff --git a/resource/rsa/private.pem b/resource/rsa/private.pem new file mode 100644 index 0000000..19afd29 --- /dev/null +++ b/resource/rsa/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDCsnMIAnRpqu3z +ZjYHmqixlG/ynV0ZfagsWvWwCsnF69bdrW/txUDRThZUUl2J8NUWmy6JyH2ex+FS +nsI05mdr6s296CXNiWu0yGrKdhT5spQEECZhpXIMc6nGSnzp+GnSruqhfxih5K9h +FpVo+gUdFh5qHLR3cv6AvylGGvSTvRFkytoLfPNdu5RwzsY2JuIpsWJCWWGUaV9t +XRuY4fwtlzBbC/jQ+113jHaCKGfm+bMZ1DU4B/cPpia2xd/uIyPQfWDmy/IezXud +xI3uH2JZOtUpw0PGuXV2bgFGzEMmxZ6fCsgzVZQe6ieVsTo6ggkQ7blPe5zKbNYP +MxJbF7exAgMBAAECggEBAIdvhS98LajX26DmaA1QG6s0G8/Egd/almMMfz4Psx54 +GUapgGQBRD6VOFk91o2/Nyv7lRsJmcEbP/WuNGCCKk1az/YcCDf7MS5YAFmIXLz5 +6ZcN+PUSFszspJwocs57HHoPbW4cMHFl2E4MXLDiwy3hlhSwlSVGnB3JXJfE5n/h +2/cHSZvcErnwJhdsExp7e4RqV4h7nJBAvU1ZnJVUbzhyjyp0wY0/JLl5G2fq0qqn +wnoeQCmqioK6eXH8zrWCnhe1W4ipcPaAkMXST7YcyPLANkxvSXG2CXxb82t671Wr +NqEdHEkly4S532S5ayuuJChwIRR7woWSZ1h43SZWkQUCgYEA+x2SMIlS4fnqWzQQ +yJs5dGfgcqh5J5Kn8J7NOry74LNLf7COL+u6fUcm4rwrBr+Q3YXWrBvyqDw4LUEB +VYhtr8oHJRbYaMjFr9A14QScBhiLjCqf8tBIFQVLqPd8OqxtsN6zSAMxGj7X68xD +t7ztPLbVv8VBk0PbShCKM5o6qpMCgYEAxnvxXCZnsBvcxMbIyq0UJwgmUueUsZfM +D0WRyEgfGKMR+6AwAvVpHaPyEzErXzPlT1TnKvwpUfagfsFsJoUrhN6czey1Np8m +reBaAAXuCRuzavRy1iMjgFuKYH0wORTR3A+fD57ZdNL5VCBN9RsQJwU3JgQZMVkf +pQ6T/oYsSysCgYEApqlxpRT/FUuw5ucfXITpFQD8ThzSjBkhrOk4fItWhkN5ED41 +oEhrdUoL3N/WDpyFoQB7Aa9q1Y1iG2bRY9swMUN8inknGCRoT894cueERed0doqz +rYvey1TAalwW7zoRcxnbEyhLJoge9jiTmRaivXD7XFOmuf6HRBjGIIlz9lECgYBs +E/VbPjZbuPA/3hZb9l7w2gk0P5HCGmwtLK6zJkJ4geM65wD9u3AfibQ5Kx742iNV +TWALEf/V97txChW/6+fElAtCPlB2i7beGzompRP2tbS+2pjlbYDZVf9FhyWJD4Mu +lvr/4Hl8mZzWaDjK7I+hD7/13Wlya5tFn2iKwbjAvQKBgQCLiRluLqvhELCyP6VL +3Os3qdb8rLbgMz9ttaxsuI7wDW8g40AkU4iwxzSQRBiCGOMtQHWS3xLxHLSAjUvG +MSdanpimagZ8aHsWo5FtL/oqucHrd1qd7SUb/r/A7HhTctwrV07j+ru2kExnNIGW +/p/L0H4Yz1blO0d6L7EF+3emKA== +-----END PRIVATE KEY----- diff --git a/resource/rsa/public.pem b/resource/rsa/public.pem new file mode 100644 index 0000000..f6e5c20 --- /dev/null +++ b/resource/rsa/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwrJzCAJ0aart82Y2B5qo +sZRv8p1dGX2oLFr1sArJxevW3a1v7cVA0U4WVFJdifDVFpsuich9nsfhUp7CNOZn +a+rNveglzYlrtMhqynYU+bKUBBAmYaVyDHOpxkp86fhp0q7qoX8YoeSvYRaVaPoF +HRYeahy0d3L+gL8pRhr0k70RZMraC3zzXbuUcM7GNibiKbFiQllhlGlfbV0bmOH8 +LZcwWwv40Ptdd4x2gihn5vmzGdQ1OAf3D6YmtsXf7iMj0H1g5svyHs17ncSN7h9i +WTrVKcNDxrl1dm4BRsxDJsWenwrIM1WUHuonlbE6OoIJEO25T3ucymzWDzMSWxe3 +sQIDAQAB +-----END PUBLIC KEY----- diff --git a/resource/template/.gitkeep b/resource/template/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tools/load/config.yml b/tools/load/config.yml new file mode 100644 index 0000000..03742c6 --- /dev/null +++ b/tools/load/config.yml @@ -0,0 +1,10 @@ +# 这个mqtt客户端主要是服务端内部处理消息使用的通道 +mqtt: + addr: 127.0.0.1:1883 + # 最好带上服务名称,变成唯一id + clientId: sagooiot20230916 + deviceLiveDuration: 60 + qos: 1 + auth: + userName: xinjy + userPassWorld: 123456 \ No newline at end of file diff --git a/tools/load/main.go b/tools/load/main.go new file mode 100644 index 0000000..3a24960 --- /dev/null +++ b/tools/load/main.go @@ -0,0 +1,193 @@ +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + mqtt "github.com/eclipse/paho.mqtt.golang" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/util/guid" + "math" + "math/rand" + "os" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" +) + +// 结构体定义 +type SysInfo struct { + Ack int `json:"ack"` +} + +type PropertyNode struct { + Value interface{} `json:"value"` + CreateTime int64 `json:"time"` +} + +// 属性上报结构体 +type ReportPropertyReq struct { + Id string `json:"id"` + Version string `json:"version"` + Sys SysInfo `json:"sys"` + Params map[string]interface{} `json:"params"` + Method string `json:"method"` +} + +type ReportPropertyReply struct { + Code int `json:"code"` + Data struct{} `json:"data"` + Id string `json:"id"` + Message string `json:"message"` + Method string `json:"method"` + Version string `json:"version"` +} + +const ( + MqttClientIdPrefix = "SagooIOT-Tools-Load" +) + +// 全局变量 +var ( + h bool + d = flag.Int("d", 5, "模拟的设备数") + m = flag.Int("m", 2, "设备多少秒发送一次") +) + +var address, mqttUserName, mqttPassword string + +func main() { + flag.Usage = usage + flag.Parse() + if h { + flag.Usage() + } + + NumGoroutines := *d + OneMessageDuration := *m + if OneMessageDuration < 1 { + fmt.Println("设备发送间隔不能小于1秒") + return + } + + address = g.Cfg().MustGet(context.Background(), "mqtt.addr", "127.0.0.1:1883").String() + mqttUserName = g.Cfg().MustGet(context.Background(), "mqtt.auth.userName", "").String() + mqttPassword = g.Cfg().MustGet(context.Background(), "mqtt.auth.userPassWorld", "").String() + + initDeviceDelays(NumGoroutines, OneMessageDuration) +} + +func initDeviceDelays(numDevices, messageInterval int) { + var wg sync.WaitGroup + var messageCount int32 + for i := 1; i <= numDevices; i++ { + wg.Add(1) + // 为每个设备计算一个随机的起始延迟,以确保它们的启动时间分散 + startDelay := rand.Intn(messageInterval * 1000) // 随机延迟,最大不超过消息间隔 + go func(deviceID, delay int) { + defer wg.Done() + time.Sleep(time.Duration(delay) * time.Millisecond) // 等待起始延迟 + fmt.Println("设备", deviceID, "已初始化", time.Now()) + + sendMessages(deviceID, messageInterval, &messageCount) + + }(i, startDelay) + } + go monitorMessageCount(&messageCount) + + wg.Wait() // 等待所有设备完成初始化 +} + +func sendMessages(id, messageInterval int, messageCount *int32) { + opts := mqtt.NewClientOptions() + opts.AddBroker(address) + opts.SetClientID(fmt.Sprintf("%s_%d", MqttClientIdPrefix+guid.S(), id)) + opts.SetUsername(mqttUserName) + opts.SetPassword(mqttPassword) + opts.SetConnectRetry(true) + opts.SetConnectRetryInterval(1 * time.Second) + opts.SetKeepAlive(30 * time.Second) + mqttClient := mqtt.NewClient(opts) + if token := mqttClient.Connect(); token.Wait() && token.Error() != nil { + panic(token.Error()) + } + defer mqttClient.Disconnect(250) + + ticker := time.NewTicker(time.Duration(messageInterval) * time.Second) + defer ticker.Stop() + + for range ticker.C { + data, _ := json.Marshal(&ReportPropertyReq{ + Id: guid.S(), + Version: "1.0", + Sys: SysInfo{ + Ack: 0, + }, + Params: map[string]interface{}{ + "va": PropertyNode{ + Value: randFloatNum(180.1000, 230.5000, 4), + CreateTime: time.Now().Unix(), + }, + "vb": PropertyNode{ + Value: randFloatNum(190.1000, 230.5000, 4), + CreateTime: time.Now().Unix(), + }, + "vc": PropertyNode{ + Value: randFloatNum(185.1000, 230.5000, 4), + CreateTime: time.Now().Unix(), + }, + }, + Method: "thing.event.property.post", + }) + + deviceNum := leftPad(id, 5) + topic := fmt.Sprintf("/sys/monipower20221103/t2022%s/thing/event/property/post", deviceNum) + + token := mqttClient.Publish(topic, 2, false, data) + token.Wait() + if token.Error() != nil { + fmt.Printf("携程 %d 发送消息失败: %s\n", id, token.Error()) + } else { + atomic.AddInt32(messageCount, 1) + } + } +} + +func monitorMessageCount(messageCount *int32) { + for { + time.Sleep(time.Second) + mc := atomic.LoadInt32(messageCount) + fmt.Printf("每秒发送消息数量: %d time:%s\n", mc, time.Now().Format("2006-01-02 15:04:05")) + atomic.StoreInt32(messageCount, 0) + } +} + +func usage() { + fmt.Fprintf(os.Stderr, `SagooIOT Device Load Version: 0.0.1 +Usage: load [-h] [-g device number] [-m send interval] + +Options: +`) + flag.PrintDefaults() +} + +func randFloatNum(min, max float64, precision int) float64 { + randomNum := min + rand.Float64()*(max-min+1e-10) + result := math.Round(randomNum*math.Pow10(precision)) / math.Pow10(precision) + resultStr := strconv.FormatFloat(result, 'f', precision, 64) + f, err := strconv.ParseFloat(resultStr, 64) + if err != nil { + return 0 + } + return f +} + +func leftPad(num, digit int) string { + str := fmt.Sprintf("%d", num) + padLen := digit - len(str) + padStr := strings.Repeat("0", padLen) + return padStr + str +} diff --git a/tools/load/readme.md b/tools/load/readme.md new file mode 100644 index 0000000..ec63ada --- /dev/null +++ b/tools/load/readme.md @@ -0,0 +1,23 @@ +# 模拟设备接入负载测试工具 + +## load工具使用说明 + +编译load工具: +```shell + go build +``` + +load的参数说明如下: + +* h:显示帮助信息 +* d:设备数量 +* m:每个设备每隔多少秒发送一条数据 + + +示例:模拟50个设备接入,每个设备每10秒发送1条数据 + +```shell + +./load -d 50 -m 10 + +``` \ No newline at end of file diff --git "a/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/TSL-monipower20221103-202312031046.json" "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/TSL-monipower20221103-202312031046.json" new file mode 100644 index 0000000..21b0f45 --- /dev/null +++ "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/TSL-monipower20221103-202312031046.json" @@ -0,0 +1 @@ +{"events":[{"desc":"111","key":"h11","level":1,"name":"h11111","outputs":[]}],"functions":[{"desc":"","inputs":[{"desc":"22","key":"channel1","name":"通道1","valueType":{"falseText":"2","trueText":"1","trueValue":true,"type":"boolean"}},{"desc":"","key":"channel2","name":"通道2","valueType":{"type":"boolean"}}],"key":"openclose","name":"open","outputs":[{"desc":"","key":"resp","name":"resp","valueType":{"type":"int"}}]},{"desc":"","inputs":[{"desc":"","key":"channel3","name":"channel3","valueType":{"type":"string"}}],"key":"openchannel","name":"测试","outputs":[{"desc":"","key":"channel4","name":"channel4","valueType":{"type":"int"}},{"desc":"","key":"ceshi","name":"测试","valueType":{"type":"int"}}]}],"key":"monipower20221103","name":"模拟测试电表2022","properties":[{"accessMode":0,"desc":"","key":"va","name":"A相电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"vb","name":"B相电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"vc","name":"C相电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"ia","name":"A相电流","valueType":{"decimals":2,"type":"float","unit":"A"}},{"accessMode":1,"desc":"","key":"ib","name":"A相电流","valueType":{"decimals":2,"type":"float","unit":"A"}},{"accessMode":1,"desc":"","key":"ic","name":"C相电流","valueType":{"decimals":2,"type":"float","unit":"A"}},{"accessMode":1,"desc":"","key":"vab","name":"AB相电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"vbc","name":"BC线电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"vca","name":"CA线电压","valueType":{"decimals":2,"type":"float","unit":"V"}},{"accessMode":1,"desc":"","key":"pa","name":"A相有功功率\t","valueType":{"decimals":2,"type":"float","unit":"kW"}},{"accessMode":1,"desc":"","key":"pb","name":"B相有功功率","valueType":{"decimals":2,"type":"float","unit":"kW"}},{"accessMode":1,"desc":"","key":"pc","name":"C相有功功率","valueType":{"decimals":2,"type":"float","unit":"kW"}}],"tags":[{"accessMode":0,"desc":"111111","key":"aa111","name":"a11\"\u003e\u003c/div\u003e\u003cscript\u003ealert('xss')\u003c/script\u003e//","valueType":{"maxLength":1000,"type":"string"}}]} \ No newline at end of file diff --git "a/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/readme.md" "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/readme.md" new file mode 100644 index 0000000..d9c288c --- /dev/null +++ "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/readme.md" @@ -0,0 +1,15 @@ +# 模拟设备数据 + +## 1. 创建产品,然后导入物模型 + +文件为:TSL-monipower20221103-202312031046.json + +## 2. 批量导入设备 +在设备管理功能,设备--》更多操作:点击批量导入设备,选择文件,导入设备 +文件为:模拟测试电表10000.xlsx + +## 3. 批量启用设备 + +在设备列表,选择所有设备,点击批量启用 + +## 4. 运行模拟设备工具load \ No newline at end of file diff --git "a/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/\346\250\241\346\213\237\346\265\213\350\257\225\347\224\265\350\241\25010000.xlsx" "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/\346\250\241\346\213\237\346\265\213\350\257\225\347\224\265\350\241\25010000.xlsx" new file mode 100644 index 0000000..0803a2b Binary files /dev/null and "b/tools/load/\346\250\241\346\213\237\346\225\260\346\215\256/\346\250\241\346\213\237\346\265\213\350\257\225\347\224\265\350\241\25010000.xlsx" differ diff --git a/tools/migration/internal/td_conn.go b/tools/migration/internal/td_conn.go deleted file mode 100644 index ead1e26..0000000 --- a/tools/migration/internal/td_conn.go +++ /dev/null @@ -1,21 +0,0 @@ -package internal - -import ( - "context" - "database/sql" - - "github.com/gogf/gf/v2/frame/g" - _ "github.com/taosdata/driver-go/v3/taosRestful" -) - -func GetConn(ctx context.Context, tdname string) *sql.DB { - driver := g.Cfg("tdengine").MustGet(ctx, tdname+".type") - dsn := g.Cfg("tdengine").MustGet(ctx, tdname+".dsn") - dbName := g.Cfg("tdengine").MustGet(ctx, tdname+".dbName") - - taos, err := sql.Open(driver.String(), dsn.String()+dbName.String()) - if err != nil { - panic(tdname + "连接失败" + err.Error()) - } - return taos -} diff --git a/tools/migration/internal/td_destination.go b/tools/migration/internal/td_destination.go deleted file mode 100644 index 883ad7f..0000000 --- a/tools/migration/internal/td_destination.go +++ /dev/null @@ -1,111 +0,0 @@ -package internal - -import ( - "context" - "database/sql" - "fmt" - "math" - "strconv" - "strings" -) - -type TdDestination struct { - db *sql.DB -} - -func NewTdDestination() *TdDestination { - db := GetConn(context.Background(), "tdengineDest") - return &TdDestination{db: db} -} - -func (s *TdDestination) CreateStables() (err error) { - tdSrc := NewTdSource() - stables, createSql, err := tdSrc.ShowCreateStable() - if err != nil || len(stables) == 0 || len(createSql) == 0 { - return - } - - for i, v := range stables { - if err = s.DropStable(v); err == nil { - _, err = s.db.Exec(createSql[i]) - } - } - return -} - -func (s *TdDestination) CreateTables() (err error) { - tdSrc := NewTdSource() - tables, createSql, err := tdSrc.ShowCreateTable() - if err != nil { - return - } - if err != nil || len(tables) == 0 || len(createSql) == 0 { - return - } - - for i, v := range tables { - if err = s.DropTable(v); err == nil { - _, err = s.db.Exec(createSql[i]) - } - } - return -} - -const pageSize = 3000 - -func (s *TdDestination) InsertData() (err error) { - tdSrc := NewTdSource() - tables, err := tdSrc.Tables() - if err != nil || tables.Len() == 0 { - return - } - - for _, rs := range tables { - tb := rs["table_name"].String() - count, _ := tdSrc.Count(tb) - if count == 0 { - continue - } - println("insert table : " + tb + ", data count: " + strconv.Itoa(count)) - - for i := 1; i <= int(math.Ceil(float64(count)/pageSize)); i++ { - data, err := tdSrc.Data(tb, i, pageSize) - if err != nil || data.Len() == 0 { - continue - } - - var ( - fed []string - vle []string - ) - fed = data[0].GMap().Keys() - - for _, row := range data { - var vs []string - for _, k := range fed { - vs = append(vs, "'"+row[k].String()+"'") - } - vle = append(vle, "("+strings.Join(vs, ",")+")") - } - - sqlStr := fmt.Sprintf("insert into %s (%s) values %s", tb, strings.Join(fed, ","), strings.Join(vle, ",")) - if _, err = GetConn(context.Background(), "tdengineDest").Exec(sqlStr); err != nil { - return err - } - } - } - - return -} - -func (s *TdDestination) DropStable(stable string) (err error) { - sqlStr := "drop stable if exists " + stable - _, err = s.db.Exec(sqlStr) - return -} - -func (s *TdDestination) DropTable(table string) (err error) { - sqlStr := "drop table if exists " + table - _, err = s.db.Exec(sqlStr) - return -} diff --git a/tools/migration/internal/td_source.go b/tools/migration/internal/td_source.go deleted file mode 100644 index 75c4e51..0000000 --- a/tools/migration/internal/td_source.go +++ /dev/null @@ -1,135 +0,0 @@ -package internal - -import ( - "context" - "database/sql" - "fmt" - "github.com/gogf/gf/v2/errors/gerror" - "time" - - "github.com/gogf/gf/v2/container/gvar" - "github.com/gogf/gf/v2/database/gdb" - "github.com/gogf/gf/v2/frame/g" -) - -type TdSource struct { - db *sql.DB -} - -func NewTdSource() *TdSource { - db := GetConn(context.Background(), "tdengineSrc") - return &TdSource{db: db} -} - -func (s *TdSource) ShowCreateStable() (stables, createSql []string, err error) { - list, err := s.Stables() - if err != nil || list.Len() == 0 { - return - } - if list == nil { - gerror.New("数据为空") - return - } - - for _, v := range list { - stable := v["stable_name"].String() - sqlStr := "show create stable " + stable - rs, err := s.GetAll(sqlStr) - if err != nil || rs.Len() == 0 { - continue - } - stables = append(stables, stable) - createSql = append(createSql, rs[0]["Create Table"].String()) - } - return -} - -func (s *TdSource) ShowCreateTable() (tables, createSql []string, err error) { - list, err := s.Tables() - if err != nil || list.Len() == 0 { - return - } - if list == nil { - return - } - - for _, v := range list { - table := v["table_name"].String() - sqlStr := "show create table " + table - rs, err := s.GetAll(sqlStr) - if err != nil || rs.Len() == 0 { - continue - } - tables = append(tables, table) - createSql = append(createSql, rs[0]["Create Table"].String()) - } - return -} - -func (s *TdSource) Stables() (rs gdb.Result, err error) { - sqlStr := "show stables" - rs, err = s.GetAll(sqlStr) - return -} - -func (s *TdSource) Tables() (rs gdb.Result, err error) { - sqlStr := "show tables" - rs, err = s.GetAll(sqlStr) - return -} - -func (s *TdSource) Count(table string) (total int, err error) { - sqlStr := "select count(*) as num from " + table - rs, err := s.GetAll(sqlStr) - if err != nil || rs.Len() == 0 { - return - } - total = rs[0]["num"].Int() - return -} - -func (s *TdSource) Data(table string, pageNum, pageSize int) (rs gdb.Result, err error) { - sqlStr := fmt.Sprintf("select * from %s limit %d, %d", table, (pageNum-1)*pageSize, pageSize) - rs, err = s.GetAll(sqlStr) - return -} - -// 超级表查询,多条数据 -func (s *TdSource) GetAll(sql string, args ...any) (rs gdb.Result, err error) { - rows, err := s.db.Query(sql, args...) - if err != nil { - return - } - defer rows.Close() - - columns, _ := rows.Columns() - - for rows.Next() { - values := make([]any, len(columns)) - for i := range values { - values[i] = new(any) - } - - err = rows.Scan(values...) - if err != nil { - return nil, err - } - - m := make(gdb.Record, len(columns)) - for i, c := range columns { - m[c] = s.Time(gvar.New(values[i])) - } - rs = append(rs, m) - } - return -} - -// REST连接时区处理 -func (s *TdSource) Time(v *g.Var) (rs *g.Var) { - if t, err := time.Parse("2006-01-02 15:04:05 +0000 UTC", v.String()); err == nil { - rs = gvar.New(t.Local().Format("2006-01-02 15:04:05")) - } else { - rs = v - } - return -} diff --git a/tools/migration/main.go b/tools/migration/main.go deleted file mode 100644 index 873c976..0000000 --- a/tools/migration/main.go +++ /dev/null @@ -1,21 +0,0 @@ -package main - -import ( - itl "github.com/sagoo-cloud/sagooiot/tools/migration/internal" -) - -func main() { - td := itl.NewTdDestination() - - if err := td.CreateStables(); err != nil { - panic(err) - } - - if err := td.CreateTables(); err != nil { - panic(err) - } - - if err := td.InsertData(); err != nil { - panic(err) - } -} diff --git a/tools/migration/readme.md b/tools/migration/readme.md deleted file mode 100644 index c7ba573..0000000 --- a/tools/migration/readme.md +++ /dev/null @@ -1,21 +0,0 @@ -# TDengine 数据库迁移工具 - -> 简单的 TDengine 数据库迁移工具。 -> 该工具在数据迁移时,会删除目标数据库的超级表、子表的表结构和数据,请注意备份!! - -使用方式:直接动行即可。 - -## 一、TDengine 配置 -1. tdengineSrc:源数据库连接配置 -2. tdengineDest:目标数据库连接配置 - -## 二、程序运行时迁移步骤说明 -1. 创建超级表 - - 获取源数据库的超级表结构 - - 删除目标数据库超级表结构 - - 创建新超级表 -2. 创建子表 - - 获取源数据库的子表结构 - - 删除目标数据库子表结构 - - 创建新子表 -3. 数据迁移 diff --git a/tools/migration/tdengine.yaml b/tools/migration/tdengine.yaml deleted file mode 100644 index e625b21..0000000 --- a/tools/migration/tdengine.yaml +++ /dev/null @@ -1,15 +0,0 @@ -# TDengine配置 -tdengineSrc: - type: "taosRestful" #http连接方式,端口是6041 - dsn: "zhgy_iot:adsafdsfa@http(101.200.200.249:6041)/" - dbName: "sagoo_iot" - -# tdengineSrc: -# type: "taosRestful" #http连接方式,端口是6041 -# dsn: "root:taosdata@http(127.0.0.1:6041)/" -# dbName: "sagoo_iot" - -tdengineDest: - type: "taosRestful" #http连接方式,端口是6041 - dsn: "root:taosdata@http(127.0.0.1:6041)/" - dbName: "sagoo_iot" \ No newline at end of file diff --git a/utility/cron/cron.go b/utility/cron/cron.go deleted file mode 100644 index dc4c150..0000000 --- a/utility/cron/cron.go +++ /dev/null @@ -1,39 +0,0 @@ -package cron - -import ( - "errors" - "github.com/robfig/cron/v3" -) - -var secondParser = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.DowOptional | cron.Descriptor) - -// ParseSpec 尝试转换定时任务表达式 -func ParseSpec(spec string) (err error) { - defer func() { - if r := recover(); r != nil { - switch x := r.(type) { - case string: - err = errors.New(x) - case error: - err = x - default: - err = errors.New("发生未知panic") - } - } - }() - _, err = secondParser.Parse(spec) - return -} - -// IsValidSpec 校验spec表达式是否合法 -func IsValidSpec(spec string) (valid bool, err error) { - err = ParseSpec(spec) - if err == nil { - //log.Infof("定时任务表达式(%+v)合法", spec) - valid = true - } else { - //log.Errorf("定时任务表达式(%+v)非法", spec) - valid = false - } - return -} diff --git a/utility/cron/cron_test.go b/utility/cron/cron_test.go deleted file mode 100644 index 608613a..0000000 --- a/utility/cron/cron_test.go +++ /dev/null @@ -1,27 +0,0 @@ -package cron - -import ( - "fmt" - "strings" - "testing" -) - -func TestIsValidSpec(t *testing.T) { - var tests = []struct{ expr, err string }{ - {"* 5 j * * *", "failed to parse int from"}, - {"@every Xm", "failed to parse duration"}, - {"@unrecognized", "unrecognized descriptor"}, - {"* * * *", "expected 5 to 6 fields"}, - {"", "empty spec string"}, - } - for _, c := range tests { - actual, err := IsValidSpec(c.expr) - if err == nil || !strings.Contains(err.Error(), c.err) { - t.Errorf("%s => expected %v, got %v", c.expr, c.err, err) - } - fmt.Println(actual) - if !actual { - fmt.Println(err.Error()) - } - } -} diff --git a/utility/excel.go b/utility/excel.go deleted file mode 100644 index 9778fe4..0000000 --- a/utility/excel.go +++ /dev/null @@ -1,115 +0,0 @@ -package utility - -import ( - "bytes" - "context" - "github.com/gogf/gf/v2/frame/g" - "github.com/tealeg/xlsx" - "io" - "log" - "os" - "path/filepath" - "reflect" - "strconv" - "time" -) - -//ToExcel 生成io.ReadSeeker 参数 titleList 为Excel表头,dataList 为数据 -func ToExcel(dataList []interface{}) (content io.ReadSeeker) { - // 生成一个新的文件 - file := xlsx.NewFile() - // 添加sheet页 - sheet, _ := file.AddSheet("Sheet1") - // 插入表头 - titleRow := sheet.AddRow() - - //获取表头 - objType := reflect.TypeOf(dataList[0]) - elem := objType.Elem() - var titleList []string - if elem.Kind() == reflect.Struct { - for i := 1; i <= elem.NumField(); i++ { - field := elem.Field(i - 1) - if field.Name != "PageReq" { - titleList = append(titleList, g.I18n().T(context.TODO(), field.Name)) - } - } - } - - for _, v := range titleList { - cell := titleRow.AddCell() - cell.Value = v - //表头字体颜色 - cell.GetStyle().Font.Color = "000000" - cell.GetStyle().Fill.BgColor = "cfe2f3" - //居中显示 - cell.GetStyle().Alignment.Horizontal = "center" - cell.GetStyle().Alignment.Vertical = "center" - } - // 插入内容 - for _, v := range dataList { - row := sheet.AddRow() - row.WriteStruct(v, -1) - } - - var buffer bytes.Buffer - _ = file.Write(&buffer) - content = bytes.NewReader(buffer.Bytes()) - return -} - -func DownloadExcel(titleList []string, dataList []interface{}, filename ...string) (string, error) { - curDir, err := os.Getwd() - - if err != nil { - return "", err - } - var fileName string - if len(filename) > 0 && filename[0] != "" { - fileName = filename[0] - } else { - curdate := time.Now().UnixNano() - fileName = strconv.FormatInt(curdate, 10) + ".xls" - } - filePath := curDir + "/public/upload/" + fileName - - err = CreateFilePath(filePath) - if err != nil { - log.Printf("%s", err.Error()) - return "", err - } - - // 生成一个新的文件 - file := xlsx.NewFile() - // 添加sheet页 - sheet, _ := file.AddSheet("Sheet1") - // 插入表头 - titleRow := sheet.AddRow() - for _, v := range titleList { - cell := titleRow.AddCell() - cell.Value = v - } - // 插入内容 - for _, v := range dataList { - row := sheet.AddRow() - row.WriteStruct(v, -1) - } - - // 在提供的路径中将文件保存到xlsx文件 - err = file.Save(filePath) - if err != nil { - return "", err - } - return fileName, nil -} - -//CreateFilePath 创建路径 -func CreateFilePath(filePath string) error { - // 路径不存在创建路径 - path, _ := filepath.Split(filePath) // 获取路径 - _, err := os.Stat(path) // 检查路径状态,不存在创建 - if err != nil || os.IsExist(err) { - err = os.MkdirAll(path, os.ModePerm) - } - return err -} diff --git a/utility/jobTask/job_task.go b/utility/jobTask/job_task.go deleted file mode 100644 index d3c6942..0000000 --- a/utility/jobTask/job_task.go +++ /dev/null @@ -1,54 +0,0 @@ -package jobTask - -import ( - "context" - "github.com/gogf/gf/v2/os/gmutex" -) - -var TimeTaskList = &taskList{ - mu: gmutex.New(), -} - -type TimeTask struct { - FuncName string - Param []string - Run func(ctx context.Context) -} - -type taskList struct { - taskList []*TimeTask - mu *gmutex.Mutex -} - -// AddTask 添加任务 -func (s *taskList) AddTask(task *TimeTask) *taskList { - if task.FuncName == "" || task.Run == nil { - return s - } - s.taskList = append(s.taskList, task) - return s -} - -// GetByName 通过方法名获取对应task信息 -func (s *taskList) GetByName(funcName string) *TimeTask { - var result *TimeTask - for _, item := range s.taskList { - if item.FuncName == funcName { - result = item - break - } - } - return result -} - -// EditParams 修改参数 -func (s *taskList) EditParams(funcName string, params []string) { - s.mu.Lock() - defer s.mu.Unlock() - for _, item := range s.taskList { - if item.FuncName == funcName { - item.Param = params - break - } - } -} diff --git a/utility/liberr/err.go b/utility/liberr/err.go deleted file mode 100644 index 55dff56..0000000 --- a/utility/liberr/err.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -* @desc:错误处理 - */ - -package liberr - -import ( - "context" - "github.com/gogf/gf/v2/frame/g" -) - -func ErrIsNil(ctx context.Context, err error, msg ...string) { - if !g.IsNil(err) { - if len(msg) > 0 { - g.Log().Error(ctx, err.Error()) - panic(msg[0]) - } else { - panic(err.Error()) - } - } -} - -func ValueIsNil(value interface{}, msg string) { - if g.IsNil(value) { - panic(msg) - } -} diff --git a/utility/notifier/sysenv_enevt.go b/utility/notifier/sysenv_enevt.go deleted file mode 100644 index 5e7b0d1..0000000 --- a/utility/notifier/sysenv_enevt.go +++ /dev/null @@ -1,56 +0,0 @@ -package notifier - -import ( - "github.com/gogf/gf/v2/net/ghttp" - "github.com/sagoo-cloud/sagooiot/utility/notifier/sysenv" - "github.com/xinjiayu/sse" - "time" -) - -func SysenvMessageEvent(r *ghttp.Request) { - - sseServer := sseserver.NewServer() - go func() { - for { - hostMsg := sseserver.SSEMessage{} - hostMsg.Event = "host" - hostMsg.Data = sysenv.GetHostInfo() - hostMsg.Namespace = "/sysenv/host" - sseServer.Broadcast <- hostMsg - - sysLoadMsg := sseserver.SSEMessage{} - sysLoadMsg.Event = "sysLoad" - sysLoadMsg.Data = sysenv.GetSysLoad() - sysLoadMsg.Namespace = "/sysenv/sysLoad" - sseServer.Broadcast <- sysLoadMsg - - cpuMsg := sseserver.SSEMessage{} - cpuMsg.Event = "cpu" - cpuMsg.Data = sysenv.GetCpuInfo() - cpuMsg.Namespace = "/sysenv/cpu" - sseServer.Broadcast <- cpuMsg - - memMsg := sseserver.SSEMessage{} - memMsg.Event = "mem" - memMsg.Data = sysenv.GetMemInfo() - memMsg.Namespace = "/sysenv/mem" - sseServer.Broadcast <- memMsg - - diskMsg := sseserver.SSEMessage{} - diskMsg.Event = "disk" - diskMsg.Data = sysenv.GetDiskInfo() - diskMsg.Namespace = "/sysenv/disk" - sseServer.Broadcast <- diskMsg - - netMsg := sseserver.SSEMessage{} - netMsg.Event = "net" - netMsg.Data = sysenv.GetNetStatusInfo() - netMsg.Namespace = "/sysenv/net" - sseServer.Broadcast <- netMsg - - time.Sleep(time.Duration(2) * time.Second) - } - }() - sseServer.ServeHTTP(r.Response.RawWriter(), r.Request) - -} diff --git a/utility/utils/date_utils.go b/utility/utils/date_utils.go deleted file mode 100644 index 2f8f86a..0000000 --- a/utility/utils/date_utils.go +++ /dev/null @@ -1,157 +0,0 @@ -package utils - -import ( - "github.com/gogf/gf/v2/os/gtime" - "strconv" - "time" -) - -// GetWeekDay 获取本周的开始时间和结束时间 -func GetWeekDay() (string, string) { - now := time.Now() - offset := int(time.Monday - now.Weekday()) - //周日做特殊判断 因为time.Monday = 0 - if offset > 0 { - offset = -6 - } - - lastoffset := int(time.Saturday - now.Weekday()) - //周日做特殊判断 因为time.Monday = 0 - if lastoffset == 6 { - lastoffset = -1 - } - - firstOfWeek := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset) - lastOfWeeK := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, lastoffset+1) - f := firstOfWeek.Unix() - l := lastOfWeeK.Unix() - return time.Unix(f, 0).Format("2006-01-02") + " 00:00:00", time.Unix(l, 0).Format("2006-01-02") + " 23:59:59" -} - -// GetBetweenDates 根据开始日期和结束日期计算出时间段内所有日期 -// 参数为日期格式,如:2020-01-01 -func GetBetweenDates(sdate, edate string) []string { - var d []string - timeFormatTpl := "2006-01-02 15:04:05" - if len(timeFormatTpl) != len(sdate) { - timeFormatTpl = timeFormatTpl[0:len(sdate)] - } - date, err := time.Parse(timeFormatTpl, sdate) - if err != nil { - // 时间解析,异常 - return d - } - date2, err := time.Parse(timeFormatTpl, edate) - if err != nil { - // 时间解析,异常 - return d - } - if date2.Before(date) { - // 如果结束时间小于开始时间,异常 - return d - } - // 输出日期格式固定 - timeFormatTpl = "2006-01-02" - date2Str := date2.Format(timeFormatTpl) - d = append(d, date.Format(timeFormatTpl)) - for { - date = date.AddDate(0, 0, 1) - dateStr := date.Format(timeFormatTpl) - d = append(d, dateStr) - if dateStr == date2Str { - break - } - } - return d -} - -// GetQuarterDay 获得当前季度的初始和结束日期 -func GetQuarterDay() (string, string) { - year := time.Now().Format("2006") - month := int(time.Now().Month()) - var firstOfQuarter string - var lastOfQuarter string - if month >= 1 && month <= 3 { - //1月1号 - firstOfQuarter = year + "-01-01 00:00:00" - lastOfQuarter = year + "-03-31 23:59:59" - } else if month >= 4 && month <= 6 { - firstOfQuarter = year + "-04-01 00:00:00" - lastOfQuarter = year + "-06-30 23:59:59" - } else if month >= 7 && month <= 9 { - firstOfQuarter = year + "-07-01 00:00:00" - lastOfQuarter = year + "-09-30 23:59:59" - } else { - firstOfQuarter = year + "-10-01 00:00:00" - lastOfQuarter = year + "-12-31 23:59:59" - } - return firstOfQuarter, lastOfQuarter -} - -// GetTimeByType 根据类型获取开始时间、结束时间及差值 1 天 2 周 3 月 4 年 -func GetTimeByType(types int) (index int, begin string, end string) { - switch types { - case 1: - begin = gtime.Now().Format("Y-m-d 00:00:00") - end = gtime.Now().Format("Y-m-d H:i:s") - index = gtime.Now().Hour() + 1 - break - case 2: - //begin, _ = GetWeekDay() - begin = gtime.Now().AddDate(0, 0, -6).Format("Y-m-d 00:00:00") - - end = gtime.Now().Format("Y-m-d H:i:s") - index = int(gtime.New(end).Sub(gtime.New(begin)).Hours()/24) + 1 - break - case 3: - //begin = gtime.Now().Format("Y-m-01 00:00:00") - begin = gtime.Now().AddDate(0, 0, -23).Format("Y-m-d 00:00:00") - //end = gtime.Now().AddDate(0, 1, 0).Format("Y-m-01 00:00:00") - end = gtime.Now().Format("Y-m-d H:i:s") - //index = gtime.Now().Day() - index = int(gtime.New(end).Sub(gtime.New(begin)).Hours()/24) + 1 - break - case 4: - begin = gtime.Now().Format("Y-01-01 00:00:00") - end = gtime.Now().AddDate(1, 0, 0).Format("Y-01-01 00:00:00") - index = gtime.Now().Month() - break - default: - begin = gtime.Now().Format("Y-m-d 00:00:00") - end = gtime.Now().Format("Y-m-d H:i:s") - index = gtime.Now().Hour() + 1 - break - } - return -} - -// GetTime 根据类型和开始时间获取时间段及长度 -func GetTime(i int, types int, begin string) (startTime string, endTime string, duration int, unit string) { - switch types { - case 1: - h, _ := time.ParseDuration(strconv.Itoa(i) + "h") - startTime = gtime.New(begin).Add(h).Format("Y-m-d H:i:s") - endTime = gtime.New(startTime).Add(time.Hour).Format("Y-m-d H:i:s") - duration = gtime.New(startTime).Hour() - unit = "时" - break - case 2, 3: - startTime = gtime.New(begin).AddDate(0, 0, i).Format("Y-m-d H:i:s") - endTime = gtime.New(startTime).AddDate(0, 0, 1).Format("Y-m-d H:i:s") - duration = gtime.New(startTime).Day() - unit = "日" - case 4: - startTime = gtime.New(begin).AddDate(0, i, 0).Format("Y-m-d H:i:s") - endTime = gtime.New(startTime).AddDate(0, 1, 0).Format("Y-m-d H:i:s") - duration = gtime.New(startTime).Month() - unit = "月" - break - default: - startTime = gtime.New(begin).Add(time.Duration(i)).Format("Y-m-d H:i:s") - endTime = gtime.New(startTime).Add(time.Hour).Format("Y-m-d H:i:s") - duration = gtime.New(startTime).Hour() - unit = "时" - break - } - return -} diff --git a/utility/utils/utils.go b/utility/utils/utils.go deleted file mode 100644 index 4bf4cc5..0000000 --- a/utility/utils/utils.go +++ /dev/null @@ -1,180 +0,0 @@ -package utils - -import ( - "context" - "fmt" - "github.com/gogf/gf/v2/crypto/gmd5" - "github.com/gogf/gf/v2/encoding/gcharset" - "github.com/gogf/gf/v2/encoding/gjson" - "github.com/gogf/gf/v2/encoding/gurl" - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/net/ghttp" - "github.com/russross/blackfriday/v2" - "io" - "net" - "os" - "path" - "sort" - "strconv" - "strings" -) - -// MarkdownToHtml 解析markdown为html -func MarkdownToHtml(mdContent string) string { - return string(blackfriday.Run([]byte(mdContent))) -} - -// EncryptPassword 密码加密 -func EncryptPassword(password, salt string) string { - return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt)) -} - -// GetDomain 获取当前请求接口域名 -func GetDomain(ctx context.Context) string { - r := g.RequestFromCtx(ctx) - pathInfo, err := gurl.ParseURL(r.GetUrl(), -1) - if err != nil { - g.Log().Error(ctx, err) - return "" - } - return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"]) -} - -// GetClientIp 获取客户端IP -func GetClientIp(ctx context.Context) string { - return g.RequestFromCtx(ctx).GetClientIp() -} - -// GetUserAgent 获取user-agent -func GetUserAgent(ctx context.Context) string { - return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent") -} - -// GetLocalIP 服务端ip -func GetLocalIP() (ip string, err error) { - var addrs []net.Addr - addrs, err = net.InterfaceAddrs() - if err != nil { - return - } - for _, addr := range addrs { - ipAddr, ok := addr.(*net.IPNet) - if !ok { - continue - } - if ipAddr.IP.IsLoopback() { - continue - } - if !ipAddr.IP.IsGlobalUnicast() { - continue - } - return ipAddr.IP.String(), nil - } - return -} - -// GetCityByIp 获取ip所属城市 -func GetCityByIp(ip string) string { - if ip == "" { - return "" - } - if ip == "[::1]" || ip == "127.0.0.1" { - return "内网IP" - } - url := "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip - bytes := g.Client().GetBytes(context.TODO(), url) - src := string(bytes) - srcCharset := "GBK" - tmp, _ := gcharset.ToUTF8(srcCharset, src) - json, err := gjson.DecodeToJson(tmp) - if err != nil { - return "" - } - if json.Get("code").Int() == 0 { - city := fmt.Sprintf("%s %s", json.Get("pro").String(), json.Get("city").String()) - return city - } else { - return "" - } -} - -// 写入文件 -func WriteToFile(fileName string, content string) error { - f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644) - if err != nil { - return err - } - n, _ := f.Seek(0, io.SeekEnd) - _, err = f.WriteAt([]byte(content), n) - defer f.Close() - return err -} - -// 文件或文件夹是否存在 -func FileIsExisted(filename string) bool { - existed := true - if _, err := os.Stat(filename); os.IsNotExist(err) { - existed = false - } - return existed -} - -// 解析路径获取文件名称及后缀 -func ParseFilePath(pathStr string) (fileName string, fileType string) { - fileNameWithSuffix := path.Base(pathStr) - fileType = path.Ext(fileNameWithSuffix) - fileName = strings.TrimSuffix(fileNameWithSuffix, fileType) - return -} - -func RemoveRepeatedElementAndEmpty(arr []int) []int { - newArr := make([]int, 0) - for _, item := range arr { - repeat := false - if len(newArr) > 0 { - for _, v := range newArr { - if v == item { - repeat = true - break - } - } - } - if repeat { - continue - } - newArr = append(newArr, item) - } - return newArr -} - -// RemoveDuplicationMap 数组去重 -func RemoveDuplicationMap(arr []string) []string { - set := make(map[string]struct{}, len(arr)) - j := 0 - for _, v := range arr { - _, ok := set[v] - if ok { - continue - } - set[v] = struct{}{} - arr[j] = v - j++ - } - return arr[:j] -} - -// Decimal 保留两位小数 -func Decimal(value float64) float64 { - value, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", value), 64) - return value -} - -// InArray 判断字符串是否存在数组中 -func InArray(target string, strArray []string) bool { - sort.Strings(strArray) - index := sort.SearchStrings(strArray, target) - if index < len(strArray) && strArray[index] == target { - return true - } - return false -} diff --git a/utility/utils/utils_test.go b/utility/utils/utils_test.go deleted file mode 100644 index 7740477..0000000 --- a/utility/utils/utils_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package utils - -import ( - "testing" - - "github.com/gogf/gf/v2/frame/g" - "github.com/gogf/gf/v2/test/gtest" -) - -func TestMarkdownToHtml(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - content := ` -## 参与贡献 - -1. 框架代码:参与框架功能开发、单元测试、ISSUE提交、反馈建议等等,https://github.com/gogf/gf -2. 开发文档:参与开发文档的撰写,便于更多的人了解、热爱并加入团队,https://github.com/gogf/gf-doc - -` - g.Dump(MarkdownToHtml(content)) - }) -} diff --git a/utility/validate/validate.go b/utility/validate/validate.go new file mode 100644 index 0000000..d3e5ba4 --- /dev/null +++ b/utility/validate/validate.go @@ -0,0 +1,15 @@ +package validate + +import ( + _ "github.com/gogf/gf/v2/frame/g" +) + +// InSlice 元素是否存在于切片中 +func InSlice[K comparable](slice []K, key K) bool { + for _, v := range slice { + if v == key { + return true + } + } + return false +}